[Jieba] 文字相似度分析:類洗錢名單掃描

筆者透過以 jieba 與 gensim 探索文本主題:五月天人生無限公司歌詞分析 文章教學

用相同架構進行洗錢名單掃描之功能,主要使用Jieba、gensim 建構模型

本實驗目的是希望透過文字自動化掃描,能更準確判斷可能洗錢名單

安裝套件

pip install jieba

pip install gensim

pip intsall wordcloud

一開始先準備好目標詞庫,這裡黑名單就是筆者的資料庫

訓練好的模型就是要準備給接下來隨意輸入一個名字,找出相似黑名單人員或是就是黑名單一員

首先先來了解一下這份黑名單常見內容為何?

在建構模型前,先了解資料,這裡共有115,263筆黑名單並且有不同的語言文字,每一列都是一筆姓名

因為名單裡會有英文、中文、拉丁文等等,這份txt會是一個utf-8編碼的.txt檔

而為了便利測試,就用jieba內建的語料庫斷詞

import jieba.analyse
from wordcloud import WordCloud
import matplotlib.pyplot as plt
#載入語料庫
jieba.set_dictionary("dict.txt.big")
value=""
#黑名單文字檔
content = open('aml_2.txt', 'rb').read()
#斷詞,選出100個主題
tags = jieba.analyse.extract_tags(content,topK=100,withWeight=True)
for tag, weight in tags:
   if value=="":
       value=tag
   else :
       value=value+" "+tag
   

接著是停用詞的應用,但因為是姓名的掃「毒」

筆者認為停用詞的部分應該不會存在於姓名之中,反而是文章、句子、語音的判斷比較會與停用詞直接的關係

這裡就不設定停用詞

直接就呼叫WordCloud,並因為姓名有中文的可能,而用一個支援中文的字型

這裡就需要事先下載好.otf

wc = WordCloud(font_path="NotoSansCJKtc-Black.otf", 
#設置字體
background_color="white", #背景顏色
max_words = 2000  #文字雲顯示最大詞數
) 
wc.generate(value)
#詞雲轉為圖片存檔
wc.to_file("sys_wordcloud.jpg")

wc.to_file(filename)會自動將產生的圖片存檔成jpg

如果要在console上看到這張圖,可以利用matplotlib.pyplot 進行顯示

#顯示詞雲
plt.imshow(wc)
plt.axis("off")
plt.figure(figsize=(10,6), dpi = 100)
plt.show()

文字雲中,字越大的就是出現越多次的內容

以下圖為例,最常出現就是名字含Jo、de字眼,而其次叫Martin的出現次數也很多

像是亞洲人會出現的姓式,在Lu 跟Chen也佔了不少比例

而因為黑名單包含公司名稱,變的也有「Company」字眼現身

未來要取名字就可以盡量避免這些「菜市場名」(誤

姓名搜尋建模

了解完資料,接下來可以開始來做姓名搜尋,首先前面提到要建模

第一步把剛斷詞的結果存成一個.dataset檔案,以利後續可以使用

wf=open("cut.dataset", mode="w",encoding="utf8")
#取得斷詞結果
with open("aml_2.txt", "r",encoding="utf8") as f:
    for line in f:
        words = jieba.cut(line)
        wf.write(" ".join(words))
wf.close()

從斷詞>同義字取代>停用詞過濾>建立自定義詞庫 是一連串建模前的資料準備

因用簡單的名單過濾,這裡跳過同意字取代、停用詞過濾,直接做自定議詞庫建立

而black_list.dict就是黑名單資料庫分析後的結果

import os
from gensim import corpora, models, similarities

stop_word_content = " ".join(stopword_set)
dictionary = corpora.Dictionary(document.split() for document in open("cut.dataset",encoding="utf8"))

stoplist = set(stop_word_content.split())
stop_ids = [dictionary.token2id[stopword] for stopword in stoplist
            if stopword in dictionary.token2id] #dictionary.token2id: 代表什麼字詞對應到什麼id,有幾個id就代表有幾維向量空間

dictionary.compactify() 
dictionary.save("black_list.dict")

建好詞庫後,先來看一下詞庫裡長什麼樣子

if (os.path.exists("black_list.dict")):
    dictionary = corpora.Dictionary.load("black_list.dict")
    #查看字典裡的資料
    for word,index in dictionary.token2id.items(): 
        print(word +" id:"+ str(index))
else:
    print("語料庫不存在")

上圖可見,每一個斷詞後的結果就會給一個唯一碼

可以幫助後續利用tf-idf的方法建立模型,利用gensim 的元件包設計好的TfidfModel

載入語料庫建模時,也是要走同義字取代>停用詞過濾二個動作,在將結果做序列化

本例一樣是略過這二步,直接將語料庫做序列化

而在載入時,一定要將encoding設在utf8,才不會有日文、中文、韓文等變亂碼的問題

# 載入語料庫(save 辭庫會有亂碼的問題)
if (os.path.exists("black_list.dict")):
    dictionary = corpora.Dictionary.load("black_list.dict")
    #打開詞庫
    texts = [[word for word in document.split() ]
             for document in open("cut.dataset",encoding="utf8")]
    #將 corpus 序列化
    corpus = [dictionary.doc2bow(text) for text in texts]
    corpora.MmCorpus.serialize("black_list.mm", corpus) # Corpus in Matrix Market format     
    print("載入語料庫完成")
else:
    print("語料庫不存在")
#建立 tfidf model
tfidf = models.TfidfModel(corpus)
# 轉為向量表示
corpus_tfidf = tfidf[corpus]
#保存tf-idf
tfidf.save(u'sys_tf.tfidf')
# 建立 LSI model
lsi = models.LsiModel(corpus_tfidf, id2word=dictionary, num_topics=100)#num_topics 
corpus_lsi = lsi[corpus_tfidf] # LSI潛在語義索引
lsi.save('black_list.lsi') #保存模型
corpora.MmCorpus.serialize('black_list.mm', corpus_lsi)

前面提到,筆者斷詞時給定了一個topK=100,代表會有100個主題

在建模時,透過gensim元件包的LsiModel function 參數num_topics 給定特徵參數數量

當給定100,即是模型的自變數會有100個並透過模型建立與訓練給定權重值

筆者認知,這二件事應該是相同之意

如果模型能抓到越多的特徵,判斷可以更加準確,但也有失真的風險

以上的動作就是把一個模型建立完成並存在black_list.mm 檔案裡

由此開始,就要把剛剛的.mm模型進行測試

# 載入語料庫(save 辭庫會有亂碼的問題)
if (os.path.exists(u"black_list.dict")):
    dictionary = corpora.Dictionary.load(u"black_list.dict")
    corpus = corpora.MmCorpus(u"black_list.mm")  
    print("載入語料庫完成")
else:
    print("語料庫不存在")

#復原模型
# 建立 tfidf model
tfidf = models.TfidfModel.load(u'sys_tf.tfidf')

lsi = models.LsiModel.load(u'black_list.lsi')  # load model

輸入一個名字>名字斷詞>模型判定>回傳相似度前五名高的名字 

#相似度計算 
#隨便輸入一串字並經過斷詞
doc="北川謙次"
words_new = jieba.cut(doc)
vec_bow = dictionary.doc2bow(words_new) # 斷詞轉換成陣列
#開始計算相似度
vec_lsi = lsi[vec_bow] # 使用建立的模型進行計算    
# 建立索引
index = similarities.MatrixSimilarity(lsi[corpus]) 
index.save("black_list.index") 

# 列出相似度前五名
sims = index[vec_lsi] 
sims = sorted(enumerate(sims), key=lambda item: -item[1])
print(sims[:5])    

#對應自建語料庫,把完整的相似名字列出
array_1= [];
fp = open("cut.dataset",encoding="utf8") 
for i, line in enumerate(fp):
    array_1.append(line)
fp.close()

for item in sims[:5]:
    print("\n相似名字:",  array_1[item [0]])
    print("相似度分數:",  array_1[1])

最後,依輸入的名字,模型列出了五個分數比較高的名子出來

這可以幫助我們判斷,跟這個名子相似的(不一定長的一樣,也許是部分相似)

是否也屬於問題相關之一,而這樣的應用還可以擴充在智慧應答等

筆者最後在git上放了一段使用一百條問答庫,進行模型建置

以相同的式建立出聊天機器人的效果

參考資訊

以 jieba 與 gensim 探索文本主題:五月天人生無限公司歌詞分析 

gensim文件

TF-IDF理解

PyLadies Taiwan