Press "Enter" to skip to content

Elasticsearch:使用运行时字段更改机器学习中的 datafeed 数据

如果你对 Elasticsearch 的机器学习还不是很熟的话,那幺请阅读我之前的文章 “ Elastic:机器学习的原理及实践 – single metric job ”。我们知道机器学习作用于索引中的一些字段。如果你使用 datafeeds,则可以在分析数据之前使用运行时字段来更改数据。 你可以将一个可选的 runtime_mappings 属性添加到你的 datafeed 中,你可以在其中指定字段类型和脚本来计算自定义表达式,而不会影响你从中检索数据的索引。

 

如果你的 datafeed 定义了运行时字段,你可以在异常检测作业中使用这些字段。 例如,你可以在一个或多个检测器的分析函数中使用运行时字段。 运行时字段会根据运行时脚本中定义的计算影响搜索性能。

 

注意:其中一些示例使用正则表达式。 默认情况下,正则表达式被禁用,因为它们绕过 Painless 针对长时间运行和内存不足的脚本提供的保护。请参考 Painless 脚本语言

 

机器学习分析区分大小写。 例如,“John” 被认为与 “john” 不同。 这是你可能考虑使用将字符串转换为大写或小写字母的脚本的原因之一。

 

在接下来的展示中,我们将使用 Elastic Stack 8.2 来进行展示。机器学习是白金版功能。如果你没有订阅,你可以在自己的安装中启动白金版试用。

 

启动白金版试用

 

我们在 Kibana 中做如下的操作:

 

 

 

 

 

这样,我们就启动了白金版试用功能。

 

创建索引

 

以下 index API 创建内容并将其添加到后续示例中使用的索引:

 

PUT /my-index-000001
{
  "mappings":{
    "properties": {
      "@timestamp": { "type": "date" },
      "aborted_count": { "type": "long" },
      "another_field": { "type": "keyword" }, 
      "clientip": { "type": "keyword" },
      "coords": {
        "properties": {
          "lat": { "type": "keyword" },
          "lon": { "type": "keyword" }
        }
      },
      "error_count": { "type": "long" },
      "query": { "type": "keyword" },
      "some_field": { "type": "keyword" },
      "tokenstring1":{ "type":"keyword" },
      "tokenstring2":{ "type":"keyword" },
      "tokenstring3":{ "type":"keyword" }
    }
  }
}

 

上面的命令创建一个叫做 my-index-000001 的索引。我们使用如下的命令来写入第一个文档:

 

PUT /my-index-000001/_doc/1
{
  "@timestamp":"2017-03-23T13:00:00",
  "error_count":36320,
  "aborted_count":4156,
  "some_field":"JOE",
  "another_field":"SMITH  ",
  "tokenstring1":"foo-bar-baz",
  "tokenstring2":"foo bar baz",
  "tokenstring3":"foo-bar-19",
  "query":"www.ml.elastic.co",
  "clientip":"123.456.78.900",
  "coords": {
    "lat" : 41.44,
    "lon":90.5
  }
}

 

在此示例中,字符串字段被映射为 keyword 字段以支持聚合。 如果你想要同一字段的全文(text)和关键字(keyword)版本,请使用多字段(multi-field)。 了解更多信息,请参考 字段

 

例子一:添加一个 runtime field – 两个数值字段相加

 

如果你对如何使用 anomaly_detectors API 来创建一个异常检测,请参阅我之前的文章 “ Elastic:使用机器学习 API 创建一个任务 ”。我们使用如下的命令来创建一个检测器:

 

PUT _ml/anomaly_detectors/test1
{
  "analysis_config":{
    "bucket_span": "10m",
    "detectors":[
      {
        "function":"mean",
        "field_name": "total_error_count" 
      }
    ]
  },
  "data_description": {
    "time_field":"@timestamp"
  },
  "datafeed_config":{
    "datafeed_id": "datafeed-test1",
    "indices": ["my-index-000001"],
    "runtime_mappings": {
      "total_error_count": { 
        "type": "long",
        "script": {
          "source": "emit(doc['error_count'].value + doc['aborted_count'].value)"
        }
      }
    }
  }
}

 

如上所示,我们在 datafeed_config 的部分针对索引 my-index-000001 中的两个数值字段 error_count 及 aborted_count 做一个加法运算。我们认为这两个错误结合起来是一个 total_error_count。它的类型为 long。这个 total_error_count 作为一个运行时字段在机器学习的任务中被 detectors 所使用。

 

此 test1 异常检测作业包含一个检测器,该检测器在均值分析函数中使用运行时字段。 在 datafeed-test1 的 datafeed 中定义了运行时字段。 它包含一个在文档中添加两个字段以产生 “总” 错误计数的脚本。

 

runtime_mappings 属性的语法与 Elasticsearch 使用的语法相同。 有关详细信息,请参阅 运行时字段

 

你可以使用以下 API 预览 datafeed 的内容:

 

GET _ml/datafeeds/datafeed-test1/_preview

 

在此示例中,API 返回以下结果,其中包含 error_count 和 aborted_count 值的总和:

 

[
  {
    "@timestamp" : 1490274000000,
    "total_error_count" : 40476
  }
]

 

注意:此示例演示了如何使用运行时字段,但它包含的数据不足,无法生成有意义的结果。

 

我们可以在 Kibana 中查看刚才已经被创建的机器学习作业:

 

 

我们也可以使用 Kibana 创建使用运行时字段的高级异常检测作业。 要将 runtime_mappings 属性添加到你的数据馈送中,你必须使用编辑 JSON 选项卡。 首先,我们为索引创建一个 Data View:

 

 

 

 

然后,我们去机器学习的界面创建一个 Advanced anomaly detection:

 

 

 

 

 

 

 

 

 

 

 

如上所示,我们可以看到 datafeed 的 preview。

 

 

 

 

 

 

 

我们可以看到已经被创建的 test2 作业。

 

例子二:把字符串连接起来

 

PUT _ml/anomaly_detectors/test3
{
  "analysis_config":{
    "bucket_span": "10m",
    "detectors":[
      {
        "function":"low_info_content",
        "field_name":"my_runtime_field" 
      }
    ]
  },
  "data_description": {
    "time_field":"@timestamp"
  },
  "datafeed_config":{
    "datafeed_id": "datafeed-test3",
    "indices": ["my-index-000001"],
    "runtime_mappings": {
      "my_runtime_field": {
        "type": "keyword",
        "script": {
          "source": "emit(doc['some_field'].value + '_' + doc['another_field'].value)" 
        }
      }
    }
  }
}
GET _ml/datafeeds/datafeed-test3/_preview

 

在上面,我们把 some_field 和 another_field 两个字段连接起来成为另外一个 my_runtime_field。上面最后一个命令的返回结果为:

 

[
  {
    "@timestamp" : 1490274000000,
    "my_runtime_field" : "JOE_SMITH  "
  }
]

 

例子三:修剪字符串

 

POST _ml/datafeeds/datafeed-test3/_update
{
  "runtime_mappings": {
    "my_runtime_field": {
      "type": "keyword",
      "script": {
        "source": "emit(doc['another_field'].value.trim())" 
      }
    }
  }
}
GET _ml/datafeeds/datafeed-test3/_preview

 

此运行时字段使用 trim() 函数从字符串中修剪额外的空白。

 

预览 datafeed API 返回以下结果,表明 “SMITH ” 已被修剪为 “SMITH”:

 

[
  {
    "@timestamp" : 1490274000000,
    "my_runtime_field" : "SMITH"
  }
]

 

例子四:将字符串转换为小写

 

POST _ml/datafeeds/datafeed-test3/_update
{
  "runtime_mappings": {
    "my_runtime_field": {
      "type": "keyword",
      "script": {
        "source": "emit(doc['some_field'].value.toLowerCase())" 
      }
    }
  }
}
GET _ml/datafeeds/datafeed-test3/_preview

 

此运行时字段使用 toLowerCase 函数将字符串转换为所有小写字母。 同样,你可以使用 toUpperCase() 函数将字符串转换为大写字母。

 

预览 datafeed  API 返回以下结果,表明 “JOE” 已转换为 “joe”:

 

[
  {
    "@timestamp" : 1490274000000,
    "my_runtime_field" : "joe"
  }
]

 

例子五:将字符串转换为大小写混合格式

 

POST _ml/datafeeds/datafeed-test3/_update
{
  "runtime_mappings": {
    "my_runtime_field": {
      "type": "keyword",
      "script": {
        "source": "emit(doc['some_field'].value.substring(0, 1).toUpperCase() + doc['some_field'].value.substring(1).toLowerCase())" 
      }
    }
  }
}
GET _ml/datafeeds/datafeed-test3/_preview

 

这个运行时字段是一个更复杂的案例操作示例。 它使用 subString() 函数将字符串的第一个字母大写并将其余字符转换为小写。

 

预览 datafeed API 返回以下结果,表明 “JOE” 已转换为 “Joe”:

 

[
  {
    "@timestamp" : 1490274000000,
    "my_runtime_field" : "Joe"
  }
]

 

例子六:替换  token

 

POST _ml/datafeeds/datafeed-test3/_update
{
  "runtime_mappings": {
    "my_runtime_field": {
      "type": "keyword",
      "script": {
        "source": "emit(/\\s/.matcher(doc['tokenstring2'].value).replaceAll('_'))" 
      }
    }
  }
}
GET _ml/datafeeds/datafeed-test3/_preview

 

此脚本使用正则表达式将空格替换为下划线。

 

预览 datafeed API 返回以下结果,表明 “foo bar baz” 已转换为 “foo_bar_baz”:

 

[
  {
    "@timestamp" : 1490274000000,
    "my_runtime_field" : "foo_bar_baz"
  }
]

 

例子七:正则表达式匹配和连接

 

POST _ml/datafeeds/datafeed-test3/_update
{
  "runtime_mappings": {
    "my_runtime_field": {
      "type": "keyword",
      "script": {
        "source": "def m = /(.*)-bar-([0-9][0-9])/.matcher(doc['tokenstring3'].value); emit(m.find() ? m.group(1) + '_' + m.group(2) : '');" 
      }
    }
  }
}
GET _ml/datafeeds/datafeed-test3/_preview

 

此脚本查找特定的正则表达式模式并将匹配的组作为连接字符串发出。 如果没有找到匹配项,它会发出一个空字符串。

 

预览 datafeed API 返回以下结果,表明 “foo-bar-19” 已转换为 “foo_19”:

 

[
  {
    "@timestamp" : 1490274000000,
    "my_runtime_field" : "foo_19"
  }
]

 

例子八:转换地理点数据

 

PUT _ml/anomaly_detectors/test4
{
  "analysis_config":{
    "bucket_span": "10m",
    "detectors":[
      {
        "function":"lat_long",
        "field_name": "my_coordinates"
      }
    ]
  },
  "data_description": {
    "time_field":"@timestamp"
  },
  "datafeed_config":{
    "datafeed_id": "datafeed-test4",
    "indices": ["my-index-000001"],
    "runtime_mappings": {
      "my_coordinates": {
        "type": "keyword",
        "script": {
          "source": "emit(doc['coords.lat'].value + ',' + doc['coords.lon'].value)"
        }
      }
    }
  }
}
GET _ml/datafeeds/datafeed-test4/_preview

 

在 Elasticsearch 中,位置数据可以存储在 geo_point 字段中,但机器学习分析本身不支持这种数据类型。 此运行时字段示例将数据转换为适当的格式。

 

预览 datafeed API 返回以下结果,表明 41.44 和 90.5 已合并为 “41.44,90.5”:

 

[
  {
    "@timestamp" : 1490274000000,
    "my_coordinates" : "41.44,90.5"
  }
]

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注