iT邦幫忙

2024 iThome 鐵人賽

DAY 0
0
自我挑戰組

重新開始 elasticsearch 系列 第 16

2024 鐵人賽 Day17: Optimization II

  • 分享至 

  • xImage
  •  

前一篇綜整了三個主要的問題,並針對其中兩個—— significant-text aggregation 的效能和 suggestion 結果單一的問題做了一點點優化,這篇來探討一下 analyzer 設定不理想的問題:

先複習一下目前的設定:

{
  "analysis": {
    "analyzer": {
      "index_analyzer": {
        "type": "custom",
        "tokenizer": "standard",
        "filter": [
          "lowercase",
          "stop"
        ],
        "char_filter": [
          "pun_filter"
        ]
      }
    },
    "char_filter": {
      "pun_filter": {
        "type": "mapping",
        "mappings": [
          "/=>",
          ".=>",
          "ー=>"
        ]
      }
    }
  }
}

先前有提到 ES 的 Text Analysis 會先經過 character filter(char_filter),再經過 tokenizer 分詞、最後經過 token filter(filter)過濾要被索引的 token。

在 char_filter 中,為了避免 standard tokenizer 把 tweet 中的網址非文字的符號當成分詞字元,因此先把 /. 移除(透過 mapping 替換成沒有字元),但這個做法蠻手工的,有可能有部分沒有處理到。

另外是可以從上一篇的結果發現,symptom 和 symptom 和 symptoms 會被視為不同的token 但這在 suggestion 中不是太有用。

這篇,我們就針對這兩個問題進行處理。

Optimize Character Filter

首先 char_filter 的部分,除了 mapping replacement filter 之外,ES 還有提供 pattern replacement ,透過 regex pattern 來決定哪些 character 會被取代掉,修改後的 analyzer 如下:

{
  "analysis": {
    "analyzer": {
      "index_analyzer": {
        "type": "custom",
        "tokenizer": "standard",
        "filter": [
          "lowercase",
          "stop"
        ],
        "char_filter": [
          "pun_filter"
        ]
      }
    },
    "char_filter": {
      "pun_filter": {
        **"type": "pattern_replace",
        "pattern": "([\\w\\s\\n]+)([\\S\\W]+?)(?=[_\\w\\s\\n]+)",
        "replacement": "$1"**
      }
    }
  }
}

這個 Pattern 主要是把 『文字或空白或換行』後接『非文字』的非文字部分去除掉,所以 http 內的符號都會一並被消除,非常的方便,副作用是其他的符號也會被消除(例如日期字串內的 -/ )。

使用其中一筆資料丟入 _analyzer api 示範如下:

GET _analyze
{
"char_filter": [
    {
      "type": "pattern_replace",
      "pattern": "([\\w\\s\\n]+)([^\\w\\s\\n]+?)(?=[_\\w\\s\\n]+)",
      "replacement": "$1"
    }
    ],

  "text":"""epal Daily Update on #COVID19.
31/3/2020 | Lockdown day 8ー

Sources: 
1. https://t.co/s6hO80l4zc, https://t.co/wzozTS77hA
2. https://t.co/36Rp6NmAo1 
3. https://t.co/ly03MoV1Vj
4. https://t.co/OxLSru8Nl4
5. https://t.co/m4aC1zRia8 https://t.co/REQiQceSco"""
}

得到的結果大致還蠻符合預期的:

{
  "tokens": [
    {
      "token": """epal Daily Update on COVID19
3132020  Lockdown day 8

Sources 
1 httpstcos6hO80l4zc httpstcowzozTS77hA
2 httpstco36Rp6NmAo1 
3 httpstcoly03MoV1Vj
4 httpstcoOxLSru8Nl4
5 httpstcom4aC1zRia8 httpstcoREQiQceSco""",
      "start_offset": 0,
      "end_offset": 254,
      "type": "word",
      "position": 0
    }
  ]
}

Optimize Token Filter

接下來是要解決重複的 suggestion word 的問題,ES 提供了一個叫做 stemmer 的 token filter,顧名思義是字根的轉換,例如複數型(-s)會被轉換成單數型,動詞過去式也會被轉換為現在式等等;示範如下:

GET _analyze
{
"tokenizer": "standard",
**"filter": {
  "type": "stemmer",
  "language": "light_english"**
},
"char_filter": [
    {
      "type": "pattern_replace",
      "pattern": "([\\w\\s\\n]+)([^\\w\\s\\n]+?)(?=[\\w\\s\\n]+)",
      "replacement": "$1"
    }
    ],
  "text": "docuemnts foxes using drunk"
}

取得的 token 如下:

{
  "tokens": [
    {
      "token": "docuemnt",
      "start_offset": 0,
      "end_offset": 9,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "fox",
      "start_offset": 10,
      "end_offset": 15,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "use",
      "start_offset": 16,
      "end_offset": 21,
      "type": "<ALPHANUM>",
      "position": 2
    },
    {
      "token": "drunk",
      "start_offset": 22,
      "end_offset": 27,
      "type": "<ALPHANUM>",
      "position": 3
    }
  ]
}

雖然大部分的狀況都符合預期,但對於變形的過去式處理似乎沒有很理想。

把上述兩個修改都放入 analyzer settings 後如下:

{
    "analysis": {
      "analyzer": {
        "index_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "stop",
            "stem_filter"
          ],
          "char_filter": [
            "pun_filter"
          ]
        }
      },
      "char_filter": {
        "pun_filter": {
          "type": "pattern_replace",
          "pattern": "([\\w\\s\\n]+)([^\\w\\s\\n]+?)(?=[\\w\\s\\n]+)",
          "replacement": "$1"
        }
      },
      "filter": {
        "stem_filter": {
          "type": "stemmer",
          "language": "light_english"
        }
      }
    }
  }

修改後一樣測試一下:

輸入: cov

輸出:['covid19 percent', 'covid19 coronaviru', 'covid19 mild', 'covid 19']

輸入: covid

輸出:['covid19 percent', 'covid19 coronaviru', 'covid19 mild', 'covid_19 coronaviru', 'covid_19 evirahealth', 'covid_19 coronaupdate']

輸入: covid sym

輸出:['covid symptom mild', 'covid symptom 19', 'covid symptom showing', 'covid symply_tacha 19', 'covid symply_tacha everythintacha']

好的,看起來又更好了一點點,下一篇繼續。


上一篇
2024 鐵人賽 Day16: Optimization I
下一篇
2024 鐵人賽 Day18: Optimization III
系列文
重新開始 elasticsearch 29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言