查詢建構指南

總覽

本節將說明如何正確設定異常偵測查詢。從較高的層級來看,您必須微調以下四種參數:

  • 選擇資料參數,指定要在我們的分析中包含哪些資料。例如:QueryDataSetRequest.dimensionNamesQueryDataSetRequest.pinnedDimensionsQueryDataSetRequest.testedInterval
  • 「Aggregation」參數會指定多個事件分組方式,以產生使用配量的值。例如:ForecastParams.aggregatedDimension
  • 預測參數,用來設定用於計算預期值的預測演算法。例如:ForecastParams.holdoutForecastParams.forecastHistoryForecastParams.minDensity
  • 敏感度調整參數,如有變更,即會增加或減少回報的異常數量、延遲時間和運算資源。例如:ForecastParams.maxPositiveRelativeChangeForecastParams.maxNegativeRelativeChangeForecastParams.forecastExtraWeight

必備條件

請按照快速入門指南中的設定操作說明,確保您執行本指南中的所有指令。

我們會查詢已經從 GDELT Project 資料載入的公開示範資料集,藉此瞭解每個參數對傳回結果的影響。

將下列查詢儲存為 query.json,並儲存在工作目錄中:

{
  dimensionNames: ["EntityLOCATION"],
  testedInterval: {
    startTime: "2019-04-15T00:00:00Z",
    length: "86400s"
  },
  forecastParams: {
    forecastHistory: "2592000s",
    holdout: 10.0,
    minDensity: 0.0,
    maxPositiveRelativeChange: 1.0,
    maxNegativeRelativeChange: 1.0,
    forecastExtraWeight: 200.0
  }
}

如快速入門指南中所述,您可以使用下列指令發出查詢:

$ gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104:query

在本指南中,我們會變更 query.json 中的不同參數,說明其對輸出內容的影響。如要這麼做,您可以使用上述的 gcurl 指令編輯副本並重新執行查詢,這些指令在快速入門導覽課程指南中會視為不規則。

選擇資料

異常偵測查詢會評估特定時間範圍內,任何資料集裡的異常狀況,即可分割部分維度的資料,並選擇性篩選部分維度的值。本節將詳細說明如何控制所有資料選取步驟。

時間間隔規格

您可以設定 QueryDataSetRequest.tested_interval,藉此指定您要針對異常行為執行哪些時間間隔。在 query.json 中,我們想要偵測的是 2019 年 4 月 15 日當週發生異常狀況:

...
  testedInterval: {
    startTime: "2019-04-15T00:00:00Z",
    length: "86400s"
  },
...

舉例來說,如果您想偵測 2019 年 4 月 15 日下午 1 點到 2 點之間發生異常情況,請改為將 testedInterval 設為:

...
  testedInterval: {
    startTime: "2019-04-15T13:00:00Z",
    length: "3600s"
  },
...

提示:設定不同的開始時間和長度,以測試 testedInterval 值,看看傳回異常情況的差異。

護板

偵測特定時間間隔內的異常狀況時,我們必須指定事件如何分類到資料配量。方法是將 dimensionNames 設為您希望跨資料集分割的一組維度。

query.json 中,我們只會按「EntityLOCATION」維度來劃分資料:dimensionNames: ["EntityLOCATION"]。結果清單中的每個配量都只是「EntityLocation」維度的值不同。

不過,如果想按不同維度區隔資料,可以指定多個維度名稱:

{
  dimensionNames: ["EntityLOCATION", "EntityORGANIZATION"],
  testedInterval: {
    startTime: "2019-04-15T00:00:00Z",
    length: "86400s"
  },
  forecastParams: {
    forecastHistory: "2592000s",
    holdout: 10.0,
    minDensity: 0.0,
    maxPositiveRelativeChange: 1.0,
    maxNegativeRelativeChange: 1.0,
    forecastExtraWeight: 50.0
  }
}

執行這項查詢時,您會發現產生的配量採用兩個指定維度「EntityLOCATION」和「EntityORGANIZATION」的值 (我們只會顯示傳回的第一個異常配量):

$ gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104:query

{
  "name": "projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104",
  "anomalyDetectionResult": {
    "anomalies": [
      {
        "dimensions": [
          {
            "name": "EntityLOCATION",
            "stringVal": "Seine"
          },
          {
            "name": "EntityORGANIZATION",
            "stringVal": "AP"
          }
        ],
        "result": {
          "holdoutErrors": {},
          "trainingErrors": {},
          "forecastStats": {
            "numAnomalies": 1
          },
          "testedIntervalActual": 344
        },
        "status": {}
      },
...

篩選

如果您只想分析一小部分的資料集,我們可以設定 pinnedDimensions,只為特定值中有特定值的事件進行篩選。

篩選依據 EntityORGANIZATION 篩選,並以 EntityLOCATION 分割 (只顯示第一個結果):

$ cat query.json

{
  dimensionNames: ["EntityLOCATION"],
  pinnedDimensions: [
  {
    name: "EntityORGANIZATION",
    stringVal: "AP"
  }
  ],
  testedInterval: {
    startTime: "2019-04-15T00:00:00Z",
    length: "86400s"
  },
  forecastParams: {
    forecastHistory: "2592000s",
    holdout: 10.0,
    minDensity: 0.0,
    maxPositiveRelativeChange: 1.0,
    maxNegativeRelativeChange: 1.0,
    forecastExtraWeight: 250.0
  }
}

$ gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104:query

{
  "name": "projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104",
  "anomalyDetectionResult": {
    "anomalies": [
      {
        "dimensions": [
          {
            "name": "EntityLOCATION",
            "stringVal": "Seine"
          }
        ],
        "result": {
          "holdoutErrors": {},
          "trainingErrors": {},
          "forecastStats": {
            "numAnomalies": 1
          },
          "testedIntervalActual": 344
        },
        "status": {}
      },
      {
        "dimensions": [
          {
            "name": "EntityLOCATION",
            "stringVal": "Ile de la Cite"
          }
        ],
        "result": {
          "holdoutErrors": {},
          "trainingErrors": {},
          "forecastStats": {
            "numAnomalies": 1
          },
          "testedIntervalActual": 282
        },
        "status": {}
      },
...

匯總

預設的匯總方法會計算片段中的事件數量。

也可以透過指定數值維度的方式指定匯總事件 方法是將 ForecastParams.aggregatedDimension 設為要加總的數字維度。

預測設定

為了計算測試時間間隔內的預期值,我們會利用預測演算法的預測演算法來預測值在測試期間應執行的操作。

注意:片段的值是由匯總方法提供。

預測記錄

時間序列包含的資料則是由 ForecastParams.forecastHistory 參數提供。

在初始範例中:

  • 我們提供 forecastHistory: "2592000s",代表我們應在 2019 年 3 月 16 日至 2019 年 4 月 15 日 (30 天) 之間擷取事件,並在這兩個日期之間產生時間序列。
  • 時間序列中的每個資料點都涵蓋等於測試時間間隔的長度 (在本例中為 86400s 至 1 天)。
  • 時間序列有 30 點,每個資料點都代表該日曆日期事件的匯總值。假設我們將匯總方法視為匯總方法,第一個時間序列的值會是 2019 年 3 月 16 日事件總數的值,2019 年 3 月 17 日的第二個事件,以此類推。

您可以查看 ForecastResult.history 欄位中傳回的時間序列的歷史值 (只有在 QueryDataSetRequest.returnTimeseries 設為 true 時才會傳回該值)。

注意:我們正在將 Timeseries 原型中的時間序列模型當做點 (定義為 TimeseriesPoint) 排序,並依時間排序並具有值 (TimeseriesPoint.value) 在 [TimeseriesPoint.time, TimeseriesPoint.time + testedInterval.length] 時間間隔期間的配量匯總值。

透過增加 ForecastParams.forecastHistory 並加入較長的記錄,您可以擷取特定季節性模式,並增加預測的精確度,因為其中包含更多資料。舉例來說,假設有每月季節性模式,將 forecastHistory 設為 30 天,我們便無法擷取這些模式,因此在這種情況下,應將 forecastHistory 我們目前分析時間序列中的多個模式 (例如要偵測每月模式的 100-300 天應該足夠,但同時也取決於實際模式)。

在初始範例中,請嘗試將 testedInterval.length 縮減為 3600s (1 小時),並將 ForecastParams.forecastHistory 增加為 8640000s (100 天)。這會增加 history 時間序列中的點數量,增加到 2400 點,使其更加詳盡,涵蓋的時間也更長。

保留/測試時間間隔

為了評估預測的品質,系統只會針對時間序列的前 X% 進行訓練。時間序列的最後一個部分會保留用於評估用途,並由 ForecastParams.holdout 參數控制。

ForecastParams.holdout 參數代表一個百分比值 (介於 0 到 100 之間),並讓我們知道系統要保留多少資料做為測試用途。在範例中,我們指定了 holdout: 10.0,因此我們會保留最後 10% 的時間序列,以依據前 90% 的預測結果來評估預測結果。

在我們初始範例中有 30 個時間序列,這表示保留 27 個資料點用於訓練,以及保留 3 個模型來進行模型測試/評估。

我們在鎖定期間測量的錯誤會在 ForecastResult.holdoutErrors 中傳回,而這些錯誤是用來計算預測下限和上限值 (testedIntervalForecastLowerBoundtestedIntervalForecastUpperBound)。訴訟保留狀態越大,我們的預測值範圍就越寬,而較低的錯誤則可使我們的界限越寬 (越接近 ForecastResult.testedIntervalForecast)。

保留值必須落在較低的百分比點 (3%-10%),但應含有足夠的資料點,因為預測預測運算作業需要這些錯誤。

最低密度

ForecastParams.minDensity 參數提供的最小密度會指定時間序列數量 (至少必須含有一個事件) 的時間序列數量,該時間序列必須包含為可能的異常情形。

這與訴訟保留很類似 (介於 0 到 100 之間)。

此處的點數會介於 testedInterval.startTime - forecastHistorytestedInterlal.startTime 之間的預期資料點數量。

例如,假設有 30 個預期時間序列 (在第一個範例中將 testedInterval.length 設定為 1 天,forecastHistory 設為 30 天),並將 minDensity 設為,我們只接受包含 24 點且具有至少一個事件的時間序列。

注意:在範例中,我們將 minDensity 設定為 0,而即使偵測到尖峰流量,也會將其設為 0。

地平線

ForecastParams.horizonTime 指定的時間範圍只是指定系統從最新間隔到未來所需時間的預測,而我們應該以過去的時間序列為依據預測這些值。

如果將 QueryDataSetRequest.returnTimeseries 設為 true,預測系統會在每個配量 ForecastResult.forecast 中傳回預測時間序列,且包含 testedInterval.startTime + testedInterval.lengthtestedInterval.startTime + testedInterval.length + horizonTime 之間的預測值。

靈敏度微調

視配量測試期間的預測界限和實際值而定,我們可能會根據敏感度參數,將異常情況歸類為異常情況。這些參數包括:

  • ForecastParameters.maxPositiveRelativeChange 會指定相較於 testedIntervalForecastUpperBound 時,實際值可以增加多少。
  • ForecastParameters.maxNegativeRelativeChange 會指定相對於 testedIntervalForecastLowerBound 時,實際值能夠減少多少
  • 比較實際值和預測界限時,ForecastParameters.forecastExtraWeight 會做為額外的權重。因此,提高權重越高,就越能忽略較小的絕對變異。

注意:我們對於正值和負值的變更有不同的參數,因為我們希望讓客戶擁有不同的配量值差,以防止頻率下降,然後停用或停用另外一個。

注意maxNegativeRelativeChange 的正值最大值為 1.0,用於正時間序列 (例如將事件數量設為匯總方法) 時,值不能會大於。

如要當做基準,請嘗試將以下機密參數設為 0:

{
  dimensionNames: ["EntityLOCATION"],
  testedInterval: {
    startTime: "2019-04-15T00:00:00Z",
    length: "86400s"
  },
  forecastParams: {
    forecastHistory: "2592000s",
    holdout: 10.0,
    minDensity: 0.0,
    maxPositiveRelativeChange: 0.0,
    maxNegativeRelativeChange: 0.0,
    forecastExtraWeight: 0.0
  }
}

這是我們可執行的最敏感查詢,系統會將這類查詢標示為含有testedIntervalActual[testedIntervalForecastLowerBoundtestedIntervalForecastUpperBound] 界線。儘管我們看起來像這樣可能會 (而且在部分應用程式中可能很有用),但實際上,我們仍會忽略這些多數的異常情況,因為通常大多都不會誤報。

執行這個基準查詢會產生約 2500 個異常狀況:

$ gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104:query \
    | grep "testedIntervalActual" | wc -l

2520

如果我們將 maxPositiveRelativeChangemaxNegativeRelativeChange 均增加至 1.0,就會發現異常情況的數量減少到 €1800 歐元,但後者會捨棄變異數較少的配量,將大量的異常問題歸類為異常狀況。

假設如果我們只關注當日新聞事件高峰,可能會增加 forecastExtraWeight,這會篩除數量偏低的配量。我們把這個值設為 200.0,這樣系統才能得出最後的 query.json

{
  dimensionNames: ["EntityLOCATION"],
  testedInterval: {
    startTime: "2019-04-15T00:00:00Z",
    length: "86400s"
  },
  forecastParams: {
    forecastHistory: "2592000s",
    holdout: 10.0,
    minDensity: 0.0,
    maxPositiveRelativeChange: 1.0,
    maxNegativeRelativeChange: 1.0,
    forecastExtraWeight: 200.0
  }
}

執行上個查詢只會產生 3 個異常狀況,所有都與聖母院消防有關,這是 2019 年 4 月 15 日新聞中最常提及的事件:

$ gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104:query \
    | grep stringVal

            "stringVal": "Ile de la Cite"
            "stringVal": "Notre Dame"
            "stringVal": "Seine"

查詢效能和資源使用量

除了降低雜訊之外,也能過濾掉不相關的聲音片段,降低異常偵測敏感度也能大幅降低查詢延遲時間和資源使用量。

提高 forecastExtraWeight 通常會導致異常區塊中的最明顯變化,同時也是提高查詢效能的主要敏感度參數。

您可以透過不同的機密參數來觀察參數對查詢效能的影響。例如:

$ cat query.json

{
  dimensionNames: ["EntityLOCATION"],
  testedInterval: {
    startTime: "2019-04-15T00:00:00Z",
    length: "86400s"
  },
  forecastParams: {
    forecastHistory: "2592000s",
    holdout: 10.0,
    minDensity: 0.0,
    maxPositiveRelativeChange: 0.0,
    maxNegativeRelativeChange: 0.0,
    forecastExtraWeight: 0.0
  }
}

$ time gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104:query \
    | grep stringVal | wc -l

2580

real 0m26.765s
user 0m0.618s
sys  0m0.132s

我們看到,最機密參數會導致 £26.7 版本的查詢內容。 降低靈敏度的速度最多可提高約 3.4 秒:

$ cat query.json

{
  dimensionNames: ["EntityLOCATION"],
  testedInterval: {
    startTime: "2019-04-15T00:00:00Z",
    length: "86400s"
  },
  forecastParams: {
    forecastHistory: "2592000s",
    holdout: 10.0,
    minDensity: 0.0,
    maxPositiveRelativeChange: 1.0,
    maxNegativeRelativeChange: 1.0,
    forecastExtraWeight: 200.0
  }
}

$ time gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104:query \
    | grep stringVal | wc -l

3

real 0m3.412s
user 0m0.681s
sys  0m0.047s

後續步驟