不要抓著枯葉哭泣,你要等待初春的新芽
????????????????????????????????????????????????????????—— 25.1.23
一、文本分類任務
定義:預先設定好一個文本類別集合,對于一篇文本,預測其所屬的類別
例如:
???????? 情感分析:
????????????????這家飯店太難吃了 —> 正類 ? ? ? ? ? ? ? ?
????????????????這家菜很好吃????????—> 負類
? ? ? ? ?領域分類:
? ? ? ? ? ? ? ? 今日A股行情大好? —>?經濟
? ? ? ? ? ? ? ? 今日湖人擊敗勇士 —>?體育
二、文本分類 — 使用場景
1.資訊文章打標簽
將大的分類再分成二級分類,進行小部分分類
2.電商評論分析
可以進一步分析,當作不同類別,分析具體的好/差評原因
3.違規檢測
涉黃、涉暴、涉恐、辱罵等
主要應用在客服、銷售對話質檢、或網站內容審查等
三、自定義類別任務
類別的定義方式是任意的
只要人基于文本能夠判斷,都可以作為分類類別
如:①?垃圾郵件分類、② 對話、文章是否與汽車交易相關、③ 文章風格是否與某作者風格一致、④ 文章是否是機器生成、⑤ 合同文本是否符合規范、⑥ 文章適合閱讀人群(未成年、中年、老年、孕婦等)
四、文本分類 — 機器學習
① 定義類別 ——> ② 收集數據 ——> ③ 模型訓練 ——> ④ 預測
1.定義類別
首先定義有幾個類別
2.收集數據
對于每個類別作數據的收集和標注
3.模型訓練
將類別數據送到一個分類器(神經網絡 / 機器學習中的其他方法)中去
4.預測
用這個分類器對一些未知文本類別的文本去預測一些類別
五、傳統機器學習算法 ① 貝葉斯算法
1.全概率公式
事件A的概率:等于在每種劃分之下,劃分事件?Bi?的概率 × 在這個劃分下事件 A 的概率
2.貝葉斯公式
①??
②?
③?
P(A|B):后驗概率,表示在已知事件B發生的情況下,事件A發生的概率
P(B|A):似然度,在事件A發生的條件下,事件B發生的概率
P(A):先驗概率,是在沒有任何關于B的信息時,對事件A發生概率的初始估計
P(B):邊緣概率,可以通過全概率公式進行計算
B事件發生下,A事件發生的概率 = A事件發生的概率 × A事件發生下,B事件發生的概率 除以 B事件發生的概率
3.貝葉斯公式的應用
求解:如果核酸檢測呈陽性,感染新冠的概率是多少?
我們假定新冠在人群中的感染率為0.1%(先驗概率:千分之一 0.001)【先驗 / 前置概率】
核酸檢測有一定誤報率,我們假定如下:
P(A) = 感染新冠
P(B) = 核酸檢測呈陽性?
P(A | B) = 核酸檢測呈陽性,確實感染新冠
P(B | A) = 感染了新冠,且檢測結果呈陽性
P(B | ^A) = 未感染新冠,且檢測結果呈陽性
計算如下:
????????P(A | B) = P(A) * P(B | A) / P (B)
? ? ? ? P(B) =?P(B | A) * P(A) + P(B | ^A) * P(^A) # 全概率公式
????????P(A | B) = P(A) * P(B | A) /【P(B | A) * P(A) + P(B | ^A) * P(^A)】
? ? ? ? ? ? ? ? ? ? ?= 0.001 × 0.99 / (0.99 × 0.001 + 0.05 × 0.999)
? ? ? ? ? ? ? ? ? ? ?≈?0.019 ≈ 0.02
4.貝葉斯公式在NLP中的應用
用貝葉斯公式處理文本分類任務
一個合理假設:文本屬于哪個類別,與文本中包含哪些詞相關
任務:知道文本中有哪些詞,預測文本屬于某類別的概率
5.貝葉斯公式 — 文本分類
假定有3個類別A1, A2, A3
一個文本S有n個詞組成,W1, W2, W3....Wn
想要計算文本S屬于A1類別的概率:P(A1|S) ?= P(A1|W1, W2, W3....Wn)
除以公共分母,得出的所有類別可能性加和為1,近似于做自動歸一化
# 貝葉斯公式
# 分母是公共的,可以不進行計算
P(A1 | S) = P(W1, W2…Wn | A1) ?* P(A1) / P(W1,W2…Wn)
P(A2 | S) = P(W1, W2…Wn | A2) ?* P(A2) / P(W1,W2…Wn)
P(A3 | S) = P(W1, W2…Wn | A3) ?* P(A3) / P(W1,W2…Wn)
# 詞的獨立性假設
P(W1, W2…Wn | A3) ?= P(W1 | A3) * P(W2 | A3) *… * P(Wn | A3)?
6.貝葉斯公式 — 代碼實現
jieba.initialize():是 ?jieba? 分詞庫中的一個函數,用于初始化分詞所需的詞典和配置。在某些情況下,特別是在多線程或分布式環境中使用?jieba
?時,顯式調用?initialize()
?可以確保分詞器正確加載詞典并優化性能。
參數 | 類型 | 說明 |
---|---|---|
enable_parallel | int ?或?False | 是否啟用并行分詞。如果設置為?True ,則啟用并行分詞;如果設置為具體的整數,表示并行分詞的線程數;默認為?False ,即不啟用并行分詞。 |
use_paddle | bool | 是否啟用 PaddlePaddle 模式。如果設置為?True ,則啟用 PaddlePaddle 模式進行分詞;默認為?False ,即不啟用該模式。使用此模式需要安裝 PaddlePaddle 庫。 |
user_dict | str | 用戶自定義詞典的路徑。通過設置此參數,可以讓?jieba ?加載指定的自定義詞典,以便在分詞時識別特定的詞匯。 |
cut_all | bool | 是否使用全模式分詞。如果設置為?True ,則使用全模式分詞,即把句子中所有的可以成詞的詞語都掃描出來;如果設置為?False ,則使用精確模式分詞,即試圖將句子最精確地切開;默認為?False 。 |
HMM | bool | 是否使用 HMM 模型來識別新詞。如果設置為?True ,則使用 HMM 模型;如果設置為?False ,則不使用 HMM 模型;默認為?True 。 |
use_parallel | bool ?或?int | 是否啟用并行分詞及并行分詞的線程數。如果設置為?True ,則啟用并行分詞,默認使用CPU核心數;如果設置為整數,則指定并行分詞的線程數;默認為?False 。 |
Ⅰ、初始化
defaultdict():Python 標準庫?collections
?模塊中的一個類,用于創建具有默認值的字典。當訪問一個不存在的鍵時,defaultdict
?會自動創建該鍵并賦予一個默認值,而不會拋出?KeyError
?異常。這在處理數據聚合、計數等場景中非常有用。
參數 | 類型 | 說明 |
---|---|---|
default_factory | 可調用對象(如函數、類等) | 用于生成默認值的工廠函數。當訪問一個不存在的鍵時,defaultdict ?會調用此工廠函數來生成默認值。常見的用法包括?int (默認值為?0 )、list (默認值為?[] )、set (默認值為?set() )等。如果未提供此參數,defaultdict ?的行為類似于普通字典,訪問不存在的鍵時會拋出?KeyError 。 |
**kwargs | 任意關鍵字參數 | 傳遞給字典的其他關鍵字參數,通常用于初始化字典。例如,可以傳遞一個可迭代對象來初始化字典的鍵值對。 |
def __init__(self, data_path):self.p_class = defaultdict(int)self.word_class_prob = defaultdict(dict)self.load(data_path)
Ⅱ、加載語料文件
defaultdict():Python 標準庫?collections
?模塊中的一個類,用于創建具有默認值的字典。當訪問一個不存在的鍵時,defaultdict
?會自動創建該鍵并賦予一個默認值,而不會拋出?KeyError
?異常。這在處理數據聚合、計數等場景中非常有用。
參數 | 類型 | 說明 |
---|---|---|
default_factory | 可調用對象(如函數、類等) | 用于生成默認值的工廠函數。當訪問一個不存在的鍵時,defaultdict ?會調用此工廠函數來生成默認值。常見的用法包括?int (默認值為?0 )、list (默認值為?[] )、set (默認值為?set() )等。如果未提供此參數,defaultdict ?的行為類似于普通字典,訪問不存在的鍵時會拋出?KeyError 。 |
**kwargs | 任意關鍵字參數 | 傳遞給字典的其他關鍵字參數,通常用于初始化字典。例如,可以傳遞一個可迭代對象來初始化字典的鍵值對。 |
set():?Python 內置函數,用于創建一個無序且不重復的集合(set
)。集合中的元素必須是可哈希的(即不可變類型),如數字、字符串、元組等。集合常用于去重、集合運算(如并集、交集、差集)等操作。
參數 | 類型 | 說明 |
---|---|---|
iterable | 可迭代對象(可選) | 用于初始化集合的可迭代對象,如列表、元組、字符串等。如果未提供,則創建一個空集合。 |
json.loads():?Python?json
?模塊中的函數,用于將 JSON 格式的字符串解析為 Python 對象(如字典、列表、字符串、數字等)。常用于處理來自網絡請求、文件讀取等的 JSON 數據。
參數 | 類型 | 說明 |
---|---|---|
s | str | 要解析的 JSON 格式字符串。 |
cls | 可選,json.JSONDecoder ?的子類 | 用于解碼的自定義解碼器類,默認為?json.JSONDecoder 。 |
object_hook | 可選,函數 | 用于自定義解碼特定類型的對象。 |
parse_float | 可選,函數 | 用于解析浮點數的函數,默認為?float 。 |
parse_int | 可選,函數 | 用于解析整數的函數,默認為?int 。 |
parse_constant | 可選,函數 | 用于解析 JSON 常量(如?null ,?true ,?false )的函數。 |
object_pairs_hook | 可選,函數 | 用于自定義解碼對象對的函數,優先級高于?object_hook 。 |
strict | 可選,bool | 如果為?True ,則在遇到非法字符時拋出異常,默認為?True 。 |
buffering | 可選,bool | 已棄用,忽略此參數。 |
jieba.lcut():jieba
?分詞庫中的函數,用于將輸入的字符串進行分詞,并返回一個列表。與?jieba.cut()
?不同,lcut()
?直接返回列表,而?cut()
?返回一個生成器。
參數 | 類型 | 說明 |
---|---|---|
sentence | str | 需要分詞的字符串。 |
cut_all | bool | 是否使用全模式分詞。True ?表示全模式,False ?表示精確模式(默認)。 |
HMM | bool | 是否使用 HMM 模型識別新詞。True ?表示使用,False ?表示不使用(默認)。 |
use_paddle | bool | 是否啟用 PaddlePaddle 模式進行分詞。需要安裝 PaddlePaddle 庫。True ?或?False 。 |
user_dict | str | 用戶自定義詞典的路徑,用于增強分詞效果。 |
union():集合(set
)對象的方法,用于返回兩個或多個集合的并集。并集包含所有出現在任一集合中的元素,且不重復。
參數 | 類型 | 說明 |
---|---|---|
*others | set ?或可迭代對象 | 一個或多個要合并到當前集合的其他集合或可迭代對象。 |
def load(self, path):self.class_name_to_word_freq = defaultdict(dict)self.all_words = set() #匯總一個詞表with open(path, encoding="utf8") as f:for line in f:line = json.loads(line)class_name = line["tag"]title = line["title"]words = jieba.lcut(title)self.all_words = self.all_words.union(set(words))self.p_class[class_name] += 1 #記錄每個類別樣本數量word_freq = self.class_name_to_word_freq[class_name]#記錄每個類別下的詞頻for word in words:if word not in word_freq:word_freq[word] = 1else:word_freq[word] += 1self.freq_to_prob()return
Ⅲ、將詞頻和樣本頻率轉化為概率
sum():Python 的內置函數,用于計算可迭代對象(如列表、元組等)中所有元素的總和。它也可以用于將一個可迭代對象中的元素累加到一個初始值上。
參數 | 類型 | 說明 |
---|---|---|
iterable | 可迭代對象(如列表、元組、集合等) | 要計算總和的可迭代對象。 |
start | int ?或?float (可選) | 累加的初始值,默認為?0 。 |
.values():?Python 字典(dict
)的方法,用于返回字典中所有值的視圖對象。這個視圖對象顯示的是字典中所有值的動態視圖,當字典的值發生變化時,視圖也會反映這些變化。?
dict():?Python 的內置函數,用于創建一個新的字典(dict
)。它可以通過多種方式初始化字典,包括從另一個字典、鍵值對的可迭代對象、關鍵字參數等。
參數 | 類型 | 說明 |
---|---|---|
object | 映射對象或可迭代對象(可選) | 如果提供,object ?應該是一個映射(如另一個字典)或包含鍵值對的可迭代對象。 |
**kwargs | 關鍵字參數(可選) | 任意數量的關鍵字參數,用于初始化字典。每個關鍵字參數的鍵和值將成為字典中的鍵值對。 |
defaultdict():Python 標準庫?collections
?模塊中的一個類,用于創建具有默認值的字典。當訪問一個不存在的鍵時,defaultdict
?會自動創建該鍵并賦予一個默認值,而不會拋出?KeyError
?異常。這在處理數據聚合、計數等場景中非常有用。
參數 | 類型 | 說明 |
---|---|---|
default_factory | 可調用對象(如函數、類等) | 用于生成默認值的工廠函數。當訪問一個不存在的鍵時,defaultdict ?會調用此工廠函數來生成默認值。常見的用法包括?int (默認值為?0 )、list (默認值為?[] )、set (默認值為?set() )等。如果未提供此參數,defaultdict ?的行為類似于普通字典,訪問不存在的鍵時會拋出?KeyError 。 |
**kwargs | 任意關鍵字參數 | 傳遞給字典的其他關鍵字參數,通常用于初始化字典。例如,可以傳遞一個可迭代對象來初始化字典的鍵值對。 |
items():Python 字典(dict
)的方法,用于返回字典中所有鍵值對的視圖對象。這個視圖對象顯示的是字典條目的動態視圖,意味著當字典發生變化時,視圖也會反映這些變化。
len():Python 的內置函數,用于返回對象(如字符串、列表、字典、集合等)的長度或項目數量。
參數 | 類型 | 說明 |
---|---|---|
object | 任何可迭代或可計數的對象(如字符串、列表、字典、集合、元組等) | 要計算長度的對象。 |
#將記錄的詞頻和樣本頻率都轉化為概率def freq_to_prob(self):#樣本概率計算total_sample_count = sum(self.p_class.values())self.p_class = dict([c, self.p_class[c] / total_sample_count] for c in self.p_class)#詞概率計算self.word_class_prob = defaultdict(dict)for class_name, word_freq in self.class_name_to_word_freq.items():total_word_count = sum(count for count in word_freq.values()) #每個類別總詞數for word in word_freq:#加1平滑,避免出現概率為0,計算P(wn|x1)prob = (word_freq[word] + 1) / (total_word_count + len(self.all_words))self.word_class_prob[class_name][word] = probself.word_class_prob[class_name]["<unk>"] = 1/(total_word_count + len(self.all_words))return
Ⅳ、計算給定單詞序列的聯合概率
.get():Python 字典(dict
)對象的一個方法,用于獲取指定鍵對應的值。如果鍵不存在于字典中,.get()
?方法可以返回一個默認值,而不是引發?KeyError
?異常。這使得?.get()
?方法在處理字典時更加安全和便捷。
#P(w1|x1) * P(w2|x1)...P(wn|x1)def get_words_class_prob(self, words, class_name):result = 1for word in words:unk_prob = self.word_class_prob[class_name]["<unk>"]result *= self.word_class_prob[class_name].get(word, unk_prob)return result
Ⅴ、計算特定事件類別x下的聯合概率
#計算P(w1, w2..wn|x1) * P(x1)def get_class_prob(self, words, class_name):#P(x1)p_x = self.p_class[class_name]# P(w1, w2..wn|x1) = P(w1|x1) * P(w2|x1)...P(wn|x1)p_w_x = self.get_words_class_prob(words, class_name)return p_x * p_w_x
Ⅵ、文本分類
jieba.lcut():jieba
?分詞庫中的函數,用于將輸入的字符串進行分詞,并返回一個列表。與?jieba.cut()
?不同,lcut()
?直接返回列表,而?cut()
?返回一個生成器。
參數 | 類型 | 說明 |
---|---|---|
sentence | str | 需要分詞的字符串。 |
cut_all | bool | 是否使用全模式分詞。True ?表示全模式,False ?表示精確模式(默認)。 |
HMM | bool | 是否使用 HMM 模型識別新詞。True ?表示使用,False ?表示不使用(默認)。 |
use_paddle | bool | 是否啟用 PaddlePaddle 模式進行分詞。需要安裝 PaddlePaddle 庫。True ?或?False 。 |
user_dict | str | 用戶自定義詞典的路徑,用于增強分詞效果。 |
append():Python 列表(list
)的方法,用于在列表的末尾添加一個新的元素。
參數 | 類型 | 說明 |
---|---|---|
object | 任意類型 | 要添加到列表末尾的對象。可以是任何數據類型,如整數、字符串、列表等。 |
sorted():Python 的內置函數,用于對可迭代對象進行排序,并返回一個新的排序后的列表。原對象不會被修改。
參數 | 類型 | 說明 |
---|---|---|
iterable | 可迭代對象 | 需要排序的對象,如列表、元組、字符串等。 |
key | 可選,函數 | 用于提取比較鍵的函數。默認為?None ,即直接比較元素。 |
reverse | 可選,布爾值 | 如果設置為?True ,則列表元素將被倒序(從大到小)排列。默認為?False 。 |
sum():Python 的內置函數,用于計算可迭代對象(如列表、元組等)中所有元素的總和。它也可以用于將一個可迭代對象中的元素累加到一個初始值上。
參數 | 類型 | 說明 |
---|---|---|
iterable | 可迭代對象(如列表、元組、集合等) | 要計算總和的可迭代對象。 |
start | int ?或?float (可選) | 累加的初始值,默認為?0 。 |
#做文本分類def classify(self, sentence):words = jieba.lcut(sentence) #切詞results = []for class_name in self.p_class:prob = self.get_class_prob(words, class_name) #計算class_name類概率results.append([class_name, prob])results = sorted(results, key=lambda x:x[1], reverse=True) #排序#計算公共分母:P(w1, w2, w3...wn) = P(w1,w2..Wn|x1)*P(x1) + P(w1,w2..Wn|x2)*P(x2) ... P(w1,w2..Wn|xn)*P(xn)#不做這一步也可以,對順序沒影響,只不過得到的不是0-1之間的概率值pw = sum([x[1] for x in results]) #P(w1, w2, w3...wn)results = [[c, prob/pw] for c, prob in results]#打印結果for class_name, prob in results:print("屬于類別[%s]的概率為%f" % (class_name, prob))return results
Ⅶ、貝葉斯文本分類實踐?
import math
import jieba
import re
import os
import json
from collections import defaultdictjieba.initialize()"""
貝葉斯分類實踐P(A|B) = (P(A) * P(B|A)) / P(B)
事件A:文本屬于類別x1。文本屬于類別x的概率,記做P(x1)
事件B:文本為s (s=w1w2w3..wn)
P(x1|s) = 文本為s,屬于x1類的概率. #求解目標#
P(x1|s) = P(x1|w1, w2, w3...wn) = P(w1, w2..wn|x1) * P(x1) / P(w1, w2, w3...wn)P(x1) 任意樣本屬于x1的概率。x1樣本數/總樣本數
P(w1, w2..wn|x1) = P(w1|x1) * P(w2|x1)...P(wn|x1) 詞的獨立性假設
P(w1|x1) x1類樣本中,w1出現的頻率公共分母的計算,使用全概率公式:
P(w1, w2, w3...wn) = P(w1,w2..Wn|x1)*P(x1) + P(w1,w2..Wn|x2)*P(x2) ... P(w1,w2..Wn|xn)*P(xn)
"""class BayesApproach:def __init__(self, data_path):self.p_class = defaultdict(int)self.word_class_prob = defaultdict(dict)self.load(data_path)def load(self, path):self.class_name_to_word_freq = defaultdict(dict)self.all_words = set() #匯總一個詞表with open(path, encoding="utf8") as f:for line in f:line = json.loads(line)class_name = line["tag"]title = line["title"]words = jieba.lcut(title)self.all_words = self.all_words.union(set(words))self.p_class[class_name] += 1 #記錄每個類別樣本數量word_freq = self.class_name_to_word_freq[class_name]#記錄每個類別下的詞頻for word in words:if word not in word_freq:word_freq[word] = 1else:word_freq[word] += 1self.freq_to_prob()return#將記錄的詞頻和樣本頻率都轉化為概率def freq_to_prob(self):#樣本概率計算total_sample_count = sum(self.p_class.values())self.p_class = dict([c, self.p_class[c] / total_sample_count] for c in self.p_class)#詞概率計算self.word_class_prob = defaultdict(dict)for class_name, word_freq in self.class_name_to_word_freq.items():total_word_count = sum(count for count in word_freq.values()) #每個類別總詞數for word in word_freq:#加1平滑,避免出現概率為0,計算P(wn|x1)prob = (word_freq[word] + 1) / (total_word_count + len(self.all_words))self.word_class_prob[class_name][word] = probself.word_class_prob[class_name]["<unk>"] = 1/(total_word_count + len(self.all_words))return#P(w1|x1) * P(w2|x1)...P(wn|x1)def get_words_class_prob(self, words, class_name):result = 1for word in words:unk_prob = self.word_class_prob[class_name]["<unk>"]result *= self.word_class_prob[class_name].get(word, unk_prob)return result#計算P(w1, w2..wn|x1) * P(x1)def get_class_prob(self, words, class_name):#P(x1)p_x = self.p_class[class_name]# P(w1, w2..wn|x1) = P(w1|x1) * P(w2|x1)...P(wn|x1)p_w_x = self.get_words_class_prob(words, class_name)return p_x * p_w_x#做文本分類def classify(self, sentence):words = jieba.lcut(sentence) #切詞results = []for class_name in self.p_class:prob = self.get_class_prob(words, class_name) #計算class_name類概率results.append([class_name, prob])results = sorted(results, key=lambda x:x[1], reverse=True) #排序#計算公共分母:P(w1, w2, w3...wn) = P(w1,w2..Wn|x1)*P(x1) + P(w1,w2..Wn|x2)*P(x2) ... P(w1,w2..Wn|xn)*P(xn)#不做這一步也可以,對順序沒影響,只不過得到的不是0-1之間的概率值pw = sum([x[1] for x in results]) #P(w1, w2, w3...wn)results = [[c, prob/pw] for c, prob in results]#打印結果for class_name, prob in results:print("屬于類別[%s]的概率為%f" % (class_name, prob))return resultsif __name__ == "__main__":path = "F:\人工智能NLP/NLP\Day7_文本分類問題\data/train_tag_news.json"ba = BayesApproach(path)query = "中國三款導彈可發射多彈頭 美無法防御很急躁"ba.classify(query)
7.貝葉斯算法的優點和缺點
缺點:
① 如果樣本不均衡會極大影響先驗概率
② 對于未見過的特征或樣本,條件概率為零,失去預測的意義(可以引入平滑)
③ 特征獨立假設只是個假設
④ 沒有考慮語序,也沒有詞義
優點:
① 簡單高效
② 一定的可解釋性
③ 如果樣本覆蓋的好,效果是不錯的
④ 訓練數據可以很好的分批處理
六、傳統機器學習算法 ② 支持向量機 SVM
SVM:support vector machine,1964年提出
屬于有監督學習 supervised learning
通過數據樣本,學習最大邊距超平面
引例:嘗試用一條線將藍色球與紅色三角分開
SVM分類器:將類別不同的樣本進行切分,并且要盡可能的讓兩類數據中最近的支持向量 距離這條分割函數最遠(目標是最大化margin,margin:分類線到兩邊最近的支持向量的距離)【尋找一個最優的決策邊界距離兩個類別的最近的樣本(稱為支持向量)最遠】
線性(直線)可分問題下的決策函數:
線性(直線)不可分問題:SVM算法中,將空間映射到更高的維度來分類非線性數據
【神經網絡中:添加激活函數來擬合非線性數據分布】
1.支持向量機 — 核函數
為了解決線性不可分問題,我們需要把輸入映射到高維,即尋找函數,使其輸出維度高于x??
例如: x = [X1, X2, X3] ?
令 = ?[X1*X1, ?X1*X2, ?X1*X3, ?X2*X1, ?X2*X2,? X2*X3, ?X3*X1, ?X3*X2, ?X3*X3] ? (對自己做笛卡爾積)
這樣x就從3維上升到9維
向高維映射如何解決線性不可分問題?
示例:
考慮一組一維數據,[-1, 0, 1] 為正樣本,[-3, -2, 2, 3]為負樣本
將x映射為【x,? x^2】后 ,可以用直線劃分,更高維度也是同理
但是這樣出現一個問題,維度過高的向量計算在進行內積運算非常耗時,而svm的求解中內積運算很頻繁
所以我們希望內有一種方法快速計算內積運算【】
核函數的意義:
兩個向量x1、x2過一個核函數之后的結果,恰好等于這兩個向量x1、x2分別通過一個高維映射然后再做內積得到的結果,基于核函數可以簡化支持向量機中的內積運算
繞過向高維空間映射的向量內積運算,直接代入計算核函數的公式,使得經過核函數運算后的向量恰好等于原向量經過向量內積運算映射到高維空間
所謂的【核函數】即為滿足條件:【】的函數,這些函數統稱為核函數
2.常見核函數
線性核函數:
多項式核函數:
高斯核函數:
雙曲正切核函數:
3.支持向量機 — 解決多分類問題
假設要解決一個K分類問題,即有K個目標類別
方式一:one vs one方式
建立 K(K - 1) / 2 個svm分類器,每個分類器負責K個類別中的兩個類別,判斷輸入樣本屬于哪個類別
對于一個待預測的樣本,使用所有分類器進行分類,最后保留被預測詞數最多的類別
例:假設類別有[A,B,C]
????????X —> SVM(A,B) —> A
? ? ? ? X —> SVM(A,C) —> A
? ? ? ? X —> SVM(B,C) —> B
最終判斷 ? X —> A
方式二:one vs rest方式
建立K個svm分類器,每個分類器負責劃分輸入樣本屬于K個類別中的某一個類別的概率,最后保留預測分值最高的類別
例:假設類別有[A,B,C]
????????X —> SVM(A,rest) —> 0.1
? ? ? ? X —> SVM(B,rest) —> 0.2
? ? ? ? X —> SVM(C,rest) —> 0.5
最終判斷:?X —> C?
4.支持向量機的優缺點
優點:
? ? ? ? ① 少數支持向量決定了最終結果,對異常值不敏感
? ? ? ? ② 對于樣本數量需求較低
? ? ? ? ③ 可以處理高維度數據
缺點:
? ? ? ? ① 樣本數量過多的時候,計算負擔很大
? ? ? ? ② 多分類任務處理起來比較麻煩
? ? ? ? ③ 核函數的選取以及參數的選取較為困難
5.代碼實現
Ⅰ、加載訓練好的模型
Word2Vec.load():加載之前保存的 Word2Vec 模型
參數 | 類型 | 說明 |
---|---|---|
filepath_or_buffer | str ?或?file-like object | 模型文件的路徑或文件對象。 |
*args | 任意其他參數 | 傳遞給底層加載機制的其他參數(通常不需要)。 |
**kwargs | 任意關鍵字參數 | 傳遞給底層加載機制的其他關鍵字參數(通常不需要)。 |
#輸入模型文件路徑
#加載訓練好的模型
def load_word2vec_model(path):model = Word2Vec.load(path)return model
Ⅱ、加載數據集
?open():打開一個文件,并返回一個文件對象,用于讀取、寫入或其他操作。
參數 | 類型 | 說明 |
---|---|---|
file | str ?或?path-like object | 要打開的文件路徑。 |
mode | str | 文件打開模式,如?'r' (讀取)、'w' (寫入)、'a' (追加)等。 |
buffering | int | 緩沖策略。 |
encoding | str | 文件編碼方式,如?'utf-8' 。 |
errors | str | 錯誤處理方式,如?'strict' 、'ignore' ?等。 |
其他參數 | — | 根據模式不同,可能有其他參數。 |
json.loads():將 JSON 格式的字符串解析為 Python 對象(如字典、列表等)。
參數 | 類型 | 說明 |
---|---|---|
s | str | 要解析的 JSON 字符串。 |
cls | 可選,json.JSONDecoder ?的子類 | 自定義解碼器類,默認為?json.JSONDecoder 。 |
object_hook | 可選,函數 | 用于自定義解碼特定類型的對象。 |
其他參數 | — | 其他可選參數,如?parse_float 、parse_int ?等。 |
append():將一個元素添加到列表的末尾。
參數 | 類型 | 說明 |
---|---|---|
object | 任意類型 | 要添加到列表末尾的對象。 |
join():將序列中的元素連接成一個字符串,元素之間使用指定的分隔符。
參數 | 類型 | 說明 |
---|---|---|
iterable | 可迭代對象 | 要連接的元素序列,如列表、元組等。 |
sep | str | 元素之間的分隔符,默認為空字符串。 |
jieba.lcut():使用 jieba 分詞庫將輸入的字符串切分成詞語列表。
參數 | 類型 | 說明 |
---|---|---|
sentence | str | 需要分詞的字符串。 |
cut_all | bool | 是否使用全模式分詞。True ?表示全模式,False ?表示精確模式(默認)。 |
HMM | bool | 是否使用 HMM 模型識別新詞。True ?表示使用,False ?表示不使用(默認)。 |
use_paddle | bool | 是否啟用 PaddlePaddle 模式進行分詞。需要安裝 PaddlePaddle 庫。True ?或?False 。 |
user_dict | str | 用戶自定義詞典的路徑,用于增強分詞效果。 |
#加載數據集
def load_sentence(path, model):sentences = []labels = []with open(path, encoding="utf8") as f:for line in f:line = json.loads(line)title, content = line["title"], line["content"]sentences.append(" ".join(jieba.lcut(title)))labels.append(line["tag"])train_x = sentences_to_vectors(sentences, model)train_y = label_to_label_index(labels)return train_x, train_y
Ⅲ、 將tag標簽轉換為類別編號
#tag標簽轉化為類別標號
def label_to_label_index(labels):return [LABELS[y] for y in labels]
Ⅳ、 文本向量化
split():將字符串按照指定的分隔符切分成子字符串列表。
參數 | 類型 | 說明 |
---|---|---|
sep | str ?或?None | 分隔符,默認為任意空白字符。 |
maxsplit | int | 最大分割次數,默認為 -1,表示不限制。 |
np.zeros():創建一個指定形狀和數據類型的全零數組。
參數 | 類型 | 說明 |
---|---|---|
shape | int ?或?tuple | 數組的形狀。 |
dtype | data-type | 數組元素的數據類型,默認為?float64 。 |
order | 'C' ?或?'F' | 內存布局方式,'C' ?行優先,'F' ?列優先,默認為?'C' 。 |
append():將一個元素添加到列表的末尾。
參數 | 類型 | 說明 |
---|---|---|
object | 任意類型 | 要添加到列表末尾的對象。 |
np.array():創建一個 NumPy 數組。
參數 | 類型 | 說明 |
---|---|---|
object | array_like | 輸入數據,可以是列表、元組、嵌套列表等。 |
dtype | data-type | 數組元素的數據類型,默認由輸入數據推斷。 |
copy | bool | 是否復制輸入數據,默認為?True 。 |
ndmin | int | 返回數組的最小維度。 |
其他參數 | — | 根據具體需求,可能有其他參數。 |
model.wv():訪問 Word2Vec 模型的詞向量(word vectors)。
參數 | 類型 | 說明 |
---|---|---|
word | str | 要獲取詞向量的單詞。 |
vector | bool | 是否返回詞向量,默認為?True 。 |
其他參數 | — | 根據具體需求,可能有其他參數。 |
#文本向量化,使用了基于這些文本訓練的詞向量
def sentences_to_vectors(sentences, model):vectors = []for sentence in sentences:words = sentence.split()vector = np.zeros(model.vector_size)for word in words:try:vector += model.wv[word]# vector = np.max([vector, model.wv[word]], axis=0)except KeyError:vector += np.zeros(model.vector_size)vectors.append(vector / len(words))return np.array(vectors)
Ⅴ、SVM分類器訓練
SVC():支持向量分類器(Support Vector Classifier),用于分類任務
參數 | 類型 | 說明 |
---|---|---|
C | float | 正則化參數,控制誤分類的懲罰力度。 |
kernel | str ?或?callable | 核函數類型,如?'linear' 、'poly' 、'rbf' 、'sigmoid' ?等。 |
degree | int | 多項式核函數的度數,默認為 3。 |
gamma | str ?或?float | 核函數的系數,'scale' ?或?'auto' ,或具體數值。 |
coef0 | float | 核函數中的獨立項。 |
其他參數 | — | 根據具體需求,可能有其他參數。 |
SVC對象.fit():訓練支持向量分類器模型。
參數 | 類型 | 說明 |
---|---|---|
X | array-like | 訓練數據的特征矩陣。 |
y | array-like | 訓練數據的目標標簽。 |
sample_weight | array-like | 每個樣本的權重。 |
其他參數 | — | 根據具體需求,可能有其他參數。 |
SVC對象.predict():使用訓練好的支持向量分類器進行預測。
參數 | 類型 | 說明 |
---|---|---|
X | array-like | 需要預測的數據特征矩陣。 |
返回值 | array | 預測的標簽。 |
classification_report():生成一個文本報告,展示主要的分類指標,如精確率(precision)、召回率(recall)、F1 分數等。
參數 | 類型 | 說明 |
---|---|---|
y_true | array-like | 真實的目標標簽。 |
y_pred | array-like | 預測的目標標簽。 |
labels | array-like | 報告中包含的標簽列表。 |
target_names | list ?或?None | 標簽的可讀名稱。 |
sample_weight | array-like | 每個樣本的權重。 |
其他參數 | — | 根據具體需求,可能有其他參數。 |
def main():model = load_word2vec_model("model.w2v")train_x, train_y = load_sentence("F:\人工智能NLP\\NLP\Day7_文本分類問題\data\\train_tag_news.json", model)test_x, test_y = load_sentence("F:\人工智能NLP\\NLP\Day7_文本分類問題\data\\valid_tag_news.json", model)classifier = SVC()classifier.fit(train_x, train_y)y_pred = classifier.predict(test_x)print(classification_report(test_y, y_pred))
Ⅵ、使用基于詞向量的SVM分類器
#!/usr/bin/env python3
#coding: utf-8#使用基于詞向量的分類器
#對比幾種模型的指標import json
import jieba
import numpy as np
from gensim.models import Word2Vec
from sklearn.metrics import classification_report
from sklearn.svm import SVC
from collections import defaultdictLABELS = {'健康': 0, '軍事': 1, '房產': 2, '社會': 3, '國際': 4, '旅游': 5, '彩票': 6, '時尚': 7, '文化': 8, '汽車': 9, '體育': 10, '家居': 11, '教育': 12, '娛樂': 13, '科技': 14, '股票': 15, '游戲': 16, '財經': 17}#輸入模型文件路徑
#加載訓練好的模型
def load_word2vec_model(path):model = Word2Vec.load(path)return model#加載數據集
def load_sentence(path, model):sentences = []labels = []with open(path, encoding="utf8") as f:for line in f:line = json.loads(line)title, content = line["title"], line["content"]sentences.append(" ".join(jieba.lcut(title)))labels.append(line["tag"])train_x = sentences_to_vectors(sentences, model)train_y = label_to_label_index(labels)return train_x, train_y#tag標簽轉化為類別標號
def label_to_label_index(labels):return [LABELS[y] for y in labels]#文本向量化,使用了基于這些文本訓練的詞向量
def sentences_to_vectors(sentences, model):vectors = []for sentence in sentences:words = sentence.split()vector = np.zeros(model.vector_size)for word in words:try:vector += model.wv[word]# vector = np.max([vector, model.wv[word]], axis=0)except KeyError:vector += np.zeros(model.vector_size)vectors.append(vector / len(words))return np.array(vectors)def main():model = load_word2vec_model("model.w2v")train_x, train_y = load_sentence("F:\人工智能NLP\\NLP\Day7_文本分類問題\data\\train_tag_news.json", model)test_x, test_y = load_sentence("F:\人工智能NLP\\NLP\Day7_文本分類問題\data\\valid_tag_news.json", model)classifier = SVC()classifier.fit(train_x, train_y)y_pred = classifier.predict(test_x)print(classification_report(test_y, y_pred))if __name__ == "__main__":main()