字符串基礎(chǔ)操作及應(yīng)用
自然語言處理簡介
做一個中文文本分類任務(wù)舱痘,首先要做的是文本的預(yù)處理,對文本進行分詞和去停用詞操作铝耻,來把字符串分割成詞與詞組合而成的字符串集合并去掉其中的一些非關(guān)鍵詞匯(像是:的频丘、地、得等)桐汤。再就是對預(yù)處理過后的文本進行特征提取。最后將提取到的特征送進分類器進行訓(xùn)練拣度。
什么是自然語言處理
NLP(Natural Language Processing,自然語言處理)當(dāng)中所謂的「自然」是為了與人造的語言(比如 C 語言冤馏, JAVA 等)區(qū)分開來,指自然形成的語言睦霎,即平時人們?nèi)粘J褂玫慕涣鞯恼Z言副女。「語言」則是人類區(qū)別其他動物的本質(zhì)特性沟涨。在所有生物中喜庞,只有人類才具有語言能力延都。人類的多種智能都與語言有著密切的關(guān)系。人類的邏輯思維以語言為形式殊者,人類的絕大部分知識也是以語言文字的形式記載和流傳下來的【辔螅「處理」則指的是對自然語言的各種處理方法與運用。
NLP 不僅是計算語言學(xué)的應(yīng)用領(lǐng)域刑然,還是計算機科學(xué)和人工智能(AI)領(lǐng)域的一個重要研究方向。NLP 主要研究人與計算機之間用自然語言進行有效通信的各種理論和方法择镇。因此,又可以說 NLP 是一門集語言學(xué)吝梅、計算機科學(xué)苏携、數(shù)學(xué)于一體的學(xué)科穿扳。
不過茫死,NLP 雖然是與語言學(xué)有關(guān)屡久, NLP 又與語言學(xué)那種純粹的研究自然語言有所區(qū)別。語言學(xué)是以自然語言為研究對象筛欢,研究語言的性質(zhì)、功能剥险、結(jié)構(gòu)、運用和歷史發(fā)展以及其他與語言有關(guān)的問題么介。NLP 則是在于研究能有效實現(xiàn)人機間自然語言通信的計算機系統(tǒng)。
總的來說鸽扁,自然語言處理可以概括為: 就是利用計算機的強大的運算能力,采用統(tǒng)計手段來對語言進行處理骡和,然后獲得需要的信息钮科,以達到最終想要的目的绵脯,而使用各種方法的一門技術(shù)。
NLP 分類
自然語言的理解和自然語言的生成悴侵。
自然語言理解是個綜合的系統(tǒng)工程,涉及了很多細分的學(xué)科巴元。
代表聲音的音系學(xué):語言中發(fā)音的系統(tǒng)化組織。
代表構(gòu)詞法的詞態(tài)學(xué):研究單詞構(gòu)成以及相互之間的關(guān)系修己。
代表語句結(jié)構(gòu)的句法學(xué):給定文本的那部分是語法正確的。
代表理解的語義句法學(xué)和語用學(xué) :給定文本的含義和目的是什么。
語言理解涉及語言光督、語境和各種語言形式的學(xué)科。但總的來說船老,自然語言理解又可以分為三個方面:
詞義分析
句法分析
語義分析
自然語言的生成則是從結(jié)構(gòu)化的數(shù)據(jù)(可以通俗理解為自然語言理解分析后的數(shù)據(jù))以讀取的方式自動生成文本馍管。主要有三個階段:
文本規(guī)劃:完成結(jié)構(gòu)化數(shù)據(jù)中的基礎(chǔ)內(nèi)容規(guī)劃咽斧。
語句規(guī)劃:從結(jié)構(gòu)化數(shù)據(jù)中組合語句來表達信息流岭洲。
實現(xiàn):產(chǎn)生語法通順的語句來表達文本盾剩。
研究與應(yīng)用
NLP 在目前大火的 AI 領(lǐng)域有著十分豐富的應(yīng)用∽に冢總體來說,自然語言處理的研究問題(主要)有下面幾種:
信息檢索:對大規(guī)模文檔進行索引酷麦。
語音識別:識別包含口語在內(nèi)的自然語言的聲學(xué)信號轉(zhuǎn)換成符合預(yù)期的信號。
機器翻譯:將一種語言翻譯成另外一種語言。
智能問答:自動回答問題轩褐。
對話系統(tǒng):通過多回合對話勤讽,跟用戶進行聊天脚牍、回答、完成某項任務(wù)驯遇。
文本分類:將文本自動歸類。
情感分析:判斷某段文本的情感傾向
文本生成:根據(jù)需求自動生成文本
自動文摘:歸納陡叠,總結(jié)文本的摘要。
相關(guān)術(shù)語
分詞:詞是 NLP 中能夠獨立活動的有意義的語言成分。即使某個中文單字也有活動的意義昵慌,但其實這些單字也是詞,屬于單字成詞淳蔼。
詞性標(biāo)注:給每個詞語的詞性進行標(biāo)注,比如 :跑/動詞存皂、美麗的/形容詞等等骤菠。
命名實體識別:從文本中識別出具有特定類別的實體。像是識別文本中的日期鹉戚,地名等等。
詞義消歧:多義詞判斷最合理的詞義却桶。
句法分析:解析句子中各個成分的依賴關(guān)系嗅剖。
指代消解:消除和解釋代詞「這個黔攒,他,你」等的指代問題赏胚。
字符串操作
字符作為文本類數(shù)據(jù)的基本單元,其在自然語言處理中的地位可以說是非常的重要典勇。而且,大部分的自然語言處理任務(wù)都是從字符上著手伤溉。
.count() 方法返回特定的子串在字符串中出現(xiàn)的次數(shù)侈询。
seq = '12345,1234,123,12,1'
seq1 = '1'
a = seq.count(seq1)
a
.strip()方法可以去除字符串首尾的指定符號。無指定時革为,默認去除空格符 ' ' 和換行符 '\n'震檩。
seq = ' 我們正在使用藍橋云課,藍橋云課學(xué)會很多!'
seq.strip()
seq.strip('!')
seq.strip(' 我們')
有時候只想要去除字符串開頭的某個字符串迂猴,但是字符串的末尾有一個同樣的字符串并不需要去掉。這時候可以使用 .lstrip() 方法。
seq = '12321'
seq.lstrip('1')
同樣疾掰,可以使用.rstrip() 方法來單獨去除末尾的字符勒葱。
seq.rstrip('1')
經(jīng)常會遇到需要將字符串拼接起來的情況,這時可以用運算符 + 來簡單暴力的拼接凯旋。
seq1 = '實'
seq2 = '驗'
seq3 = '樓'
seq = seq1 + seq2 + seq3
seq
seq1 = ''
seq = ['實', '驗', '樓']
# 將 seq 里面的每個字符拼接起來
for n in seq:
seq1 += n
seq1
需要將字符串用特定的符號拼接起來的字符的時候,可以用 .join() 方法來進行拼接谐鼎。
seq = ['2018', '10', '31']
seq = '-'.join(seq) # 用 '-' 拼接
seq
seq = ['實', '驗', '樓']
seq = ''.join(seq) # 用''空字符拼接
seq
當(dāng)想要比較兩個字符串的大小時,這里需要加載 operator 工具草戈,它是 Python 的標(biāo)準(zhǔn)庫。不需要額外下載费韭,直接通過 import 調(diào)用即可。operator 從左到右第一個字符開始,根據(jù)設(shè)定的規(guī)則比較鲤屡,返回布爾值( True,F(xiàn)alse )堰汉。
判斷 a < b 型:
import operator
seq1 = '字符串 1 號'
seq2 = '字符串 2 號'
operator.lt(seq1, seq2)
除了使用 operator 之外,還有一種方法更簡便: 直接使用運算符比較就乓。
直接使用運算符比較 a < b:
seq1 < seq2
判斷 a <= b:
operator.le(seq1, seq2)
seq1 <= seq2
判斷 a == b:
operator.eq(seq1, seq2)
seq1 == seq2
判斷 a != b:
operator.ne(seq1, seq2)
seq1 != seq2
判斷 a > b:
operator.gt(seq1, seq2)
seq1 > seq2
判斷 a >=b:
operator.ge(seq1, seq2)
seq1 >= seq2
在處理英文類文本的時候會遇到需要將文本全部轉(zhuǎn)化為大寫或者小寫的時候。使用 .upper() 和 .lower() 可以很方便的完成這個任務(wù)。
使用 .upper() 將文本轉(zhuǎn)化為大寫屯援。
seq = 'appLE'
seq = seq.upper()
seq
使用 .lower()將文本轉(zhuǎn)化為小寫。
seq = 'APPle'
seq = seq.lower()
seq
為了查找到某段字符串當(dāng)中某個子串的位置信息徘铝,有兩種方法。一種是.index ,一種是 .find甲锡。 兩種方法都可實現(xiàn)這個功能,不同的是 index 如果未找到的話缸废,會報錯,而 find 未找到的則會返回 -1 值。
.find() 方法姆泻,在序列當(dāng)中找到子串的起始位置四苇。PS:第一個位置是 0 。
seq = '這個是一段字符串'
seq1 = '字符串'
seq.find(seq1)
.find() 方法榆骚,字符串中不存在子串返回 -1
seq2 = '無'
seq.find(seq2)
seq3 = '字符串'
seq.index(seq3)
seq4 = '無'
seq.index(seq4)
當(dāng)想要切分字符串時苫纤,有兩種常用的方法喊废。第一種是直用序列截取的方法乍赫。這種方法十分的簡單惋增,就是根據(jù)順序來截取序列上你想要的某些片段。
想要截取 [這是字符串]讲弄, 中的‘這是字符’的時候避除。[0:4] 中 : 左邊的 0 的意思是從序列位置 0(從 0 開始數(shù))怎披,: 右邊的 4 意思是截取到第 4 個字符(但并不包括從零開始數(shù)的第 4 個字符)。
seq = '這是字符串'
seq1 = seq[0:4]
seq1
截取某個字符的時候可以通過這樣瓶摆。
seq2 = seq[0]
seq2
seq3 = seq[1:4]
seq3
如果結(jié)合字符串拼接的操作,還能把截出來的字符串拼接起來凉逛。
seq = '小了白了兔'
a = seq[0]
b = seq[2]
c = seq[4]
seq1 = a+b+c
seq1
需要把一個字符串按照某個字符切分開處理。比如[今天天氣很好群井,我們出去玩],要把兩句話以 ','切開成兩句話咧党。split()函數(shù)可以完成這個操作,函數(shù)返回一個由切分好的字符串組成的列表。
seq = '今天天氣很好欲账,我們出去玩'
seq = seq.split('惹苗,')
seq
seq = '2018-11-11'
seq = seq.split('-')
seq
seq = 'I have an apple'
seq = seq.split(' ')
seq
seq = '號外玷或!號外氛濒!特大新聞'
seq = seq.split('醒串!')
seq
需要翻轉(zhuǎn)字符串的時候,那么我們直接用序列操作,直接以上面截取序列的方法畜伐,但是按照逆向的來截取實現(xiàn)翻轉(zhuǎn)。
seq = '12345'
seq = seq[::-1]
seq
遇到需要判斷某子串在字符串中是否出現(xiàn),并根據(jù)判斷做出后續(xù)操作的情況茄蚯。可以用 in 來作出判斷如失,若存在則返回 True跟压,不存在則返回 False效扫,然后配合 if 闪盔,作出后續(xù)操作靠抑。
seq = 'abcdef'
'a' in seq
seq = '你的名字真好聽!'
'的' in seq
in 關(guān)鍵字可以用在任何容器對象上米绕,判斷一個子對象是否存在于容器當(dāng)中率拒,并不局限于判斷字符串是否存在某子串,還可以用在其他容器對象例如 list,tuple,set 等類型。
nums = [1, 2, 3, 4]
2 in nums
seq = 'abcd'
n = 0
if 'a' in seq:
n += 1
n
有時需要把字符串中的某段字符串用另一段字符串代替抛腕,比如 2018-01-01 中的 - 用 '/' 代替敢伸。我們可以用到 .replace(a,b) 揭保,他可以將某字符串中的 a 字符串 替換成 b 字符串汁掠。下面來實現(xiàn)一下。
seq = '2018-11-11'
seq = seq.replace('-', '/')
seq
seq = '等會你還要來做實驗嗎匣砖?'
seq = seq.replace('嗎?', '哦车要!')
seq
seq = '小了白了兔'
seq = seq.replace('了', '')
seq
當(dāng)遇到需要判斷字符串是否以某段字符開頭的時候晃跺。比如想要判斷 ‘a(chǎn)bcdefg’ 是否以 'a' 開頭瑞信。可以用 .startswish() 方法后频。
seq = 'abcdefg'
seq.startswith('a')
seq = '我在藍橋云課'
seq.startswith('me')
同樣的方法竖伯,我們可以用 .endswith() 來確定字符串是否以某段字符串結(jié)尾贺辰。
seq = 'abcd'
seq.endswith('d')
seq = '我在藍橋云課'
seq.endswith('嘍')
有時候,當(dāng)想要檢查字符串的構(gòu)成棚品,像是檢查字符串是否由純數(shù)字構(gòu)成。
seq = 's123'
seq.isdigit()
seq = '123'
seq.isdigit()
正則表達式
正則表達式是用于處理字符串的強大工具沧侥,它由一個特殊的字符序列構(gòu)成一定的規(guī)則可霎,根據(jù)這個規(guī)則可以幫你檢查字符串是否與這個規(guī)則的字符串匹配。
在下面這幾個日期信息中宴杀,如果想要提取其中的年份信息:
A : 2018/01/01
B : 2018-01-01
C : 2018.01.01
D :01/01/2018
觀察 A癣朗,B,C旺罢,D 四個人的輸入旷余,因為每個人輸入習(xí)慣的不同,導(dǎo)致了格式方面的不統(tǒng)一扁达。這個時候正卧,我們無法通過 Python 提供的字符串前 4 位 [0:4] 的方法來提取每段序列當(dāng)中的年份信息。這時候跪解,可以考慮用到正則表達式炉旷。我們設(shè)計一個正則表達式 [0-9]{4} ,這個正則表達式代表的是 0 到 9 的數(shù)字連續(xù) 4 次惠遏。
Python 對于正則表達式的支持砾跃,一般是通過 re 模塊來提供骏啰。re 模塊是 Python 自帶的標(biāo)準(zhǔn)庫节吮,直接 import 加載就可以了,不需要另外下載第三方庫判耕。
首先透绩,我們需要通過 re.compile() 將編寫好的正則表達式編譯為一個實例。
# 加載 re 模塊
import re
# 將正則表達式編寫成實例
pattern = re.compile(r'[0-9]{4}')
pattern
然后我們對字符串進行匹配壁熄,并對匹配結(jié)果進行操作帚豪。這里,我們要用到的是 re.search() 方法草丧。 這個方法是將正則表達式與字符串進行匹配狸臣,如果找到第一個符合正則表達式的結(jié)果,就會返回昌执,然后匹配結(jié)果存入group()中供后續(xù)操作烛亦。
import re
pattern = re.compile(r'[0-9]{4}')
time = '2018-01-01'
# 用剛剛編譯好的 pattern诈泼,去匹配 time
match = pattern.search(time)
# 匹配結(jié)果存放在 group()當(dāng)中的
match.group()
可以看到我們的正則表達式匹配到了 2018 這個符合我們規(guī)則(連續(xù)4個字符 都是 0-9的數(shù)字)的結(jié)果。 現(xiàn)在我們根據(jù)上面兩段代碼的內(nèi)容煤禽,設(shè)計一個小程序來提取這種不規(guī)則日期當(dāng)中的年份信息铐达。
import re
pattern = re.compile(r'[0-9]{4}')
times = ('2018/01/01', '01/01/2019', '01.2017.01')
for time in times:
match = pattern.search(time)
if match:
print('年份有:', match.group())
.findall():這個方法可以找到符合正則表達式的所有匹配結(jié)果。這里我們使用了 \d 規(guī)則的正則表達式檬果,這個正則表達式可以替我們識別數(shù)字瓮孙。
import re
pattern = re.compile(r'\d')
pattern.findall('o1n2m3k4')
同樣的方法,我們編寫一個 \D 正則表達式选脊,這個可以匹配一個非數(shù)字字符杭抠。
pattern = re.compile('\D')
pattern.findall('1A2B3C4D')
.match() 方法與 .search() 方法類似,只匹配一次恳啥,并且只從字符串的開頭開始匹配祈争。同樣,match 結(jié)果也是存在 group() 當(dāng)中角寸。
# 不止是規(guī)則菩混,字符也是可以單獨作為正則表達式使用。
pattern = re.compile('c')
pattern.match('comcdc').group()
.match() 只從開頭匹配扁藕。若匹配不成功沮峡,則 group()不會有內(nèi)容。
# 我們編寫了字符 1 作為正則表達式去匹配亿柑。
pattern = re.compile('1')
pattern.match('abcdefg1').group()
正則表達式的應(yīng)用場景十分廣泛:在去除網(wǎng)頁類文本語料庫中 html 符號邢疙,去除一些聊天的表情符號像是 Orz ,T_T 等等有著十分重要的應(yīng)用望薄。
中英文分詞方法及實現(xiàn)
英文分詞
在語言理解中疟游,詞是最小的能夠獨立活動的有意義的粒度。由詞到句痕支,由句成文颁虐。因此,將詞確定下來是理解自然語言處理的第一步卧须,只有跨越了這一步另绩,才能進行后續(xù)任務(wù)。
英文原文: i have a pen,i have an apple!
英文分詞結(jié)果: i , have , a , pen , i , have , an , apple , !
通過上面的英文分詞例子花嘶,可以發(fā)現(xiàn)英文文本詞與詞之間有空格或者標(biāo)點符號笋籽,如果想要對這種普通的英文文本進行分詞的話是不需要什么算法支撐,只需暴力拆分即可椭员,即直接通過空格或者標(biāo)點來將文本進行分開就可以完成英文分詞车海。
按空格切分無限次:
a = 'i have a pen'
a1 = a.split(' ')
a1
按空格切分 1 次:
a2 = a.split(' ', 1)
a2
按 have 切分 1 次:
a3 = a.split('have', 1)
a3
實現(xiàn)對多個英文文本分詞,要求同時以 , 隘击, . 侍芝, ? 喘沿, ! , 五個符號分詞竭贩。
首先對原文本以其中一個規(guī)則切分后蚜印,再對分好后的文本進行下一個規(guī)則的切分,再對分好的文本進行切分留量,直到按 5 個規(guī)則切分完成窄赋,最后將切分好的詞添加進 tokenized_text 并返回。
def tokenize_english_text(text):
# 首先楼熄,我們按照標(biāo)點來分句
# 先建立一個空集用來忆绰,用來將分好的詞添加到里面作為函數(shù)的返回值
tokenized_text = []
# 一個 text 中可能不止一個內(nèi)容,我們對每個文本單獨處理并存放在各自的分詞結(jié)果中可岂。
for data in text:
# 建立一個空集來存儲每一個文本自己的分詞結(jié)果错敢,每對 data 一次操作我們都歸零這個集合
tokenized_data = []
# 以 '.'分割整個句子,對分割后的每一小快 s:
for s in data.split('.'):
# 將's'以 '缕粹?'分割稚茅,分割后的每一小快 s2:
for s1 in s.split('?'):
# 同樣的道理分割 s2,
for s2 in s1.split('!'):
# 同理
for s3 in s2.split(','):
# 將 s3 以空格分割平斩,然后將結(jié)果添加到 tokenized_data 當(dāng)中
tokenized_data.extend(
s4 for s4 in s3.split(' ') if s4 != '')
# 括號內(nèi)的部分拆開理解
# for s4 in s3.split(' '):
# if s4!='': 這一步是去除空字符''亚享。注意與' ' 的區(qū)別。
# 將每個 tokenized_data 分別添加到 tokenized_text 當(dāng)中
tokenized_text.append(tokenized_data)
return tokenized_text
a = ['i am a boy?i am a boy ! i am a boy,i', 'god is a girl', 'i love you!']
result = tokenize_english_text(a)
result
中文分詞
通過計算機將句子轉(zhuǎn)化成詞的表示绘面,自動識別句子中的詞欺税,在詞與詞之間加入邊界分隔符,分割出各個詞匯揭璃。這個切詞過程就叫做中文分詞晚凿。
中文原文: 我們今天要做實驗。
中文分詞結(jié)果:我們 | 今天 | 要 | 做 | 實驗 瘦馍。
由于中文的結(jié)構(gòu)與印歐體系語種有很大的差異歼秽。在中文中,文本是由連續(xù)的字序列構(gòu)成扣墩,詞和詞之間沒有天然的分隔符哲银。不同分詞方法的結(jié)果會影響到詞性扛吞,句法等問題呻惕。分詞作為一個方法,如果運用場景不同滥比,要求不同亚脆,最終對任務(wù)達到的效果也不同。
困難之一:歧義盲泛。
原文:以前喜歡一個人濒持,現(xiàn)在喜歡一個人
這里有兩個「一個人」键耕,但是代表的意思完全不一樣。
困難之二:分詞界限柑营。
原文:這杯水還沒有冷
分詞一: 這 | 杯 | 水 | 還 | 沒有 | 冷
分詞二: 這 | 杯 | 水 | 還沒 | 有 | 冷
分詞三: 這 | 杯 | 水 | 還沒有 | 冷
中文分詞這個概念自提出以來屈雄,經(jīng)過多年的發(fā)展,主要可以分為三個方法:
機械分詞方法官套;
統(tǒng)計分詞方法酒奶;
兩種結(jié)合起來的分詞方法。
機械分詞方法
機械分詞方法又叫做基于規(guī)則的分詞方法:這種分詞方法按照一定的規(guī)則將待處理的字符串與一個詞表詞典中的詞進行逐一匹配奶赔,若在詞典中找到某個字符串惋嚎,則切分,否則不切分站刑。機械分詞方法按照匹配規(guī)則的方式另伍,又可以分為:正向最大匹配法,逆向最大匹配法和雙向匹配法三種绞旅。
正向最大匹配法
正向最大匹配法(Maximum Match Method摆尝,MM 法)是指從左向右按最大原則與詞典里面的詞進行匹配。假設(shè)詞典中最長詞是 m 個字因悲,那么從待切分文本的最左邊取 m 個字符與詞典進行匹配结榄,如果匹配成功,則分詞囤捻。如果匹配不成功臼朗,那么取 m?1 個字符與詞典匹配,一直取直到成功匹配為止蝎土。
通過用一個簡單的例子來講一下 MM 法的過程:
句子:中華民族從此站起來了
詞典:"中華"视哑,"民族","從此"誊涯,"站起來了"
使用 MM 法分詞:
第一步:詞典中最長是 4 個字挡毅,所以我們將 【中華民族】 取出來與詞典進行匹配,匹配失敗暴构。
第二步:于是跪呈,去掉 【族】,以 【中華民】 進行匹配取逾,匹配失敗
第三步:去掉 【中華民】 中的 【民】耗绿,以 【中華】 進行匹配,匹配成功砾隅。
第四步:在帶切分句子中去掉匹配成功的詞误阻,待切分句子變成 【民族從此站起來了】。
第五步:重復(fù)上面的第 1 - 4 步驟
第六步:若最后一個詞語匹配成功,結(jié)束究反。
最終句子被分成:【中華 | 民族 | 從此 | 站起來了】
逆向最大匹配法
逆向最大匹配法( Reverse Maximum Match Method, RMM 法)的原理與正向法基本相同寻定,唯一不同的就是切分的方向與 MM 法相反。逆向法從文本末端開始匹配精耐,每次用末端的最長詞長度個字符進行匹配狼速。
另外,由于漢語言結(jié)構(gòu)的問題卦停,里面有許多偏正短語唐含,即結(jié)構(gòu)是:
定語 + 中心詞(名、代):(祖國)大地沫浆、(一朵)茶花捷枯、(前進)的步伐。
狀語 + 中心詞(動专执、形):(很)好看淮捆、(獨立)思考、(慢慢)地走本股。
如果采用逆向匹配法攀痊,可以適當(dāng)提高一些精確度。換句話說拄显,使用逆向匹配法要比正向匹配法的誤差要小苟径。
雙向最大匹配法
雙向最大匹配法(Bi-direction Matching Method ,BMM)則是將正向匹配法得到的分詞結(jié)果與逆向匹配法得到的分詞結(jié)果進行比較躬审,然后按照最大匹配原則棘街,選取次數(shù)切分最少的作為結(jié)果。
實現(xiàn)正向最大匹配法
整個正向最大匹配算法的流程圖:
算法步驟:
導(dǎo)入分詞詞典 dic承边,待分詞文本 text遭殉,創(chuàng)建空集 words 。
遍歷分詞詞典博助,找到最長詞的長度险污,max_len_word 。
將待分詞文本從左向右取 max_len=max_len_word 個字符作為待匹配字符串 word 富岳。
將 word 與詞典 dic 匹配
若匹配失敗蛔糯,則 max_len = max_len - 1 ,然后
重復(fù) 3 - 4 步驟
匹配成功窖式,將 word 添加進 words 當(dāng)中蚁飒。
去掉待分詞文本前 max_len 個字符
重置 max_len 值為 max_len_word
重復(fù) 3 - 8 步驟
返回列表 words
創(chuàng)建一個簡單的詞典 dic 和測試文本 text 。
首先創(chuàng)建一個測試文本 text:
# 文本
text = '我們是共產(chǎn)主義的接班人'
text
創(chuàng)建一個詞典 dic:
# 詞典
dic = ('我們', '是', '共產(chǎn)主義', '的', '接班', '人', '你', '我', '社會', '主義')
dic
我們需要遍歷詞典來求出最長詞的長度:
# 初始最長詞長度為 0
max_len_word0 = 0
for key in dic:
# 若當(dāng)前詞長度大于 max_len_word脖镀,則將 len(key)值賦值給 max_len_word
if len(key) > max_len_word0:
max_len_word0 = len(key)
max_len_word0
通過循環(huán)來完成 MM 法:
sent = text
words = [] # 建立一個空數(shù)組來存放分詞結(jié)果:
max_len_word = max_len_word0
# 判斷 text 的長度是否大于 0飒箭,如果大于 0 則進行下面的循環(huán)
while len(sent) > 0:
# 初始化想要取的字符串長度
# 按照最長詞長度初始化
word_len = max_len_word
# 對每個字符串可能會有(max_len_word)次循環(huán)
for i in range(0, max_len_word):
# 令 word 等于 text 的前 word_len 個字符
word = sent[0:word_len]
# 為了便于觀察過程狼电,我們打印一下當(dāng)前分割結(jié)果
print('用 【', word, '】 進行匹配')
# 判斷 word 是否在詞典 dic 當(dāng)中
# 如果不在詞典當(dāng)中
if word not in dic:
# 則以 word_len - 1
word_len -= 1
# 清空 word
word = []
# 如果 word 在詞典當(dāng)中
else:
# 更新 text 串起始位置
sent = sent[word_len:]
# 為了方便觀察過程蜒灰,我們打印一下當(dāng)前結(jié)果
print('【{}】 匹配成功弦蹂,添加進 words 當(dāng)中'.format(word))
print('-'*50)
# 把匹配成功的word添加進上面創(chuàng)建好的words當(dāng)中
words.append(word)
# 清空word
word = []
統(tǒng)計分詞方法
基于的字典的方法實現(xiàn)比較簡單,而且性能也還不錯强窖。但是其有一個缺點凸椿,那就是不能切分 未登錄詞 ,也就是不能切分字典里面沒有的詞翅溺。為解決這一問題脑漫,于是有學(xué)者提出了基于統(tǒng)計的分詞方法。
目前基于統(tǒng)計的分詞方法大體可以分兩種方法:
語料統(tǒng)計方法
序列標(biāo)注方法
語料統(tǒng)計方法
對于語料統(tǒng)計方法可以這樣理解:我們已經(jīng)有一個由很多個文本組成的的語料庫 D 咙崎,假設(shè)現(xiàn)在對一個句子【我有一個蘋果】進行分詞优幸。其中兩個相連的字 【蘋】【果】在不同的文本中連續(xù)出現(xiàn)的次數(shù)越多,就說明這兩個相連字很可能構(gòu)成一個詞【蘋果】褪猛。與此同時 【個】【蘋】 這兩個相連的詞在別的文本中連續(xù)出現(xiàn)的次數(shù)很少网杆,就說明這兩個相連的字不太可能構(gòu)成一個詞【個蘋】。所以伊滋,我們就可以利用這個統(tǒng)計規(guī)則來反應(yīng)字與字成詞的可信度碳却。當(dāng)字連續(xù)組合的概率高過一個臨界值時,就認為該組合構(gòu)成了一個詞語笑旺。
序列標(biāo)注方法
序列標(biāo)注方法則將中文分詞看做是一個序列標(biāo)注問題昼浦。首先,規(guī)定每個字在一個詞語當(dāng)中有著 4 個不同的位置筒主,詞首 B关噪,詞中 M,詞尾 E乌妙,單字成詞 S色洞。我們通過給一句話中的每個字標(biāo)記上述的屬性,最后通過標(biāo)注來確定分詞結(jié)果冠胯。
例如:我今天要去實驗室
標(biāo)注后得到:我/S 今/B 天/E 要/S 去/S 實/B 驗/M 室/E
標(biāo)注序列是:S B E S S B M E
找到 S 火诸、B 、 E 進行切詞:S / B E / S / S / B M E /
所以得到的切詞結(jié)果是:我 / 今天 / 要 / 去 / 實驗室
在訓(xùn)練時荠察,輸入中文句子和對應(yīng)的標(biāo)注序列置蜀,訓(xùn)練完成得到一個模型。在測試時悉盆,輸入中文句子盯荤,通過模型運算得到一個標(biāo)注序列。然后通過標(biāo)注序列來進行切分句子焕盟。
在統(tǒng)計學(xué)習(xí)方法中秋秤,可以用于序列標(biāo)注任務(wù)的方法有很多。例如,隱馬爾可夫模型灼卢,條件隨機場等绍哎。
可以在 Python 用第三方的中文分詞工具 jieba ,來替我們在實際應(yīng)用中省掉訓(xùn)練分詞隱馬可夫模型的繁瑣步驟鞋真。并且崇堰,jieba 工具用的是隱馬爾可夫模型與字典相結(jié)合的方法,比直接單獨使用隱馬爾可夫模型來分詞效率高很多涩咖,準(zhǔn)確率也高很多海诲。
目前在實際應(yīng)用中,jieba 分詞是使用率很高的一款工具檩互。不僅使用起來十分的方便特幔、快速,而且分詞效果也比較理想闸昨。
在使用 jieba 進行分詞時蚯斯,有三種模式可選:
全模式
精確模式
搜索引擎模式
import jieba
全模式:
string = '我來到北京清華大學(xué)'
seg_list = jieba.cut(string, cut_all=True)
jieba 是將分詞后的結(jié)果存放在生成器當(dāng)中的。
seg_list
無法直接顯示零院,若想要顯示溉跃,可以下面這樣。用 ‘|’ 把生成器中的詞串起來顯示告抄。這個方法在下面提到的精確模式和搜索引擎模式中同樣適用撰茎。
seg_list = jieba.cut(string, cut_all=False)
'|'.join(seg_list)
搜索引擎模式:
seg_list = jieba.cut_for_search(string)
'|'.join(seg_list)
全模式和搜索引擎模式,jieba 會把全部可能組成的詞都打印出來打洼。在一般的任務(wù)當(dāng)中龄糊,我們使用默認的精確模式就行了,在模糊匹配時募疮,則需要用到全模式或者搜索引擎模式炫惩。
我們試著對一篇長文本作分詞。首先阿浓,導(dǎo)入某一段文本他嚷。
text = '市場有很多機遇但同時也充滿殺機,野蠻生長和快速發(fā)展中如何慢慢穩(wěn)住底盤芭毙,駕馭風(fēng)險筋蓖,保持起伏沖撞在合理的范圍,特別是新興行業(yè)退敦,領(lǐng)軍企業(yè)更得有胸懷和大局粘咖,需要在競爭中保持張弛有度,促成行業(yè)建立同盟和百花爭艷的健康持續(xù)的多贏局面侈百,而非最后比的是誰狠瓮下,比的是誰更有底線翰铡,劣幣驅(qū)逐良幣,最終誰都逃不了要還的讽坏。'
text
適用精確模式對文本進行分詞:
a = jieba.cut(text, cut_all=False)
'|'.join(a)
jieba 在某些特定的情況下分詞锭魔,可能表現(xiàn)不是很好。比如一篇非常專業(yè)的醫(yī)學(xué)論文震缭,含有一些特定領(lǐng)域的專有名詞赂毯。不過战虏,為了解決此類問題拣宰, jieba 允許用戶自己添加該領(lǐng)域的自定義詞典,我們可以提前把這些詞加進自定義詞典當(dāng)中烦感,來增加分詞的效果巡社。調(diào)用的方法是:jieba.load_userdic()。
自定義詞典的格式要求每一行一個詞手趣,有三個部分晌该,詞語,詞頻(詞語出現(xiàn)的頻率)绿渣,詞性(名詞朝群,動詞……)。其中中符,詞頻和詞性可省略姜胖。用戶自定義詞典可以直接用記事本創(chuàng)立即可,但是需要以 utf-8 編碼模式保存淀散。 格式像下面這樣:
兇許 1 a
腦斧 2 b
福蝶 c
小局 4
海疼
除了使用 jieba.load_userdic() 函數(shù)在分詞開始前加載自定義詞典之外右莱,還有兩種方法在可以在程序中動態(tài)修改詞典。
使用 add_word(word, freq=None, tag=None) 和 del_word(word) 可在程序中動態(tài)修改詞典档插。
使用 suggest_freq(segment, tune=True) 可調(diào)節(jié)單個詞語的詞頻慢蜓,使其能(或不能)被分出來。
使用自定義詞典郭膛,有時候可以取得更好的效果晨抡,例如「今天天氣不錯」這句話,本應(yīng)該分出「今天」则剃、「天氣」耘柱、「不錯」三個詞,而來看一下直接使用結(jié)巴分詞的結(jié)果:
string = '今天天氣不錯'
seg_list = jieba.cut(string, cut_all=False)
'|'.join(seg_list)
可以看到結(jié)果并沒有被完整分割忍级,這時候就可以加載自定義的詞典了帆谍,將「今天」和「天氣」兩個詞語添加到詞典中,并重新分詞:
jieba.suggest_freq(('今天', '天氣'), True)
seg_list = jieba.cut(string, cut_all=False)
'|'.join(seg_list)
也可以從詞典直接刪除該詞語:
jieba.del_word('今天天氣')
seg_list = jieba.cut(string, cut_all=False)
'|'.join(seg_list)
還有一種情況是「臺中」總是被切成「臺」和「中」轴咱,因為 P(臺中) < P(臺)×P(中)汛蝙,“臺中”詞頻不夠?qū)е缕涑稍~概率較低烈涮,這時候可以添加詞典,強制調(diào)高詞頻窖剑。
string = '臺中'
seg_list = jieba.cut(string, cut_all=False)
'|'.join(seg_list)
強制調(diào)高「臺中」的詞頻坚洽,使它被分為一個詞:
jieba.add_word('臺中')
seg_list = jieba.cut(string, cut_all=False)
'|'.join(seg_list)
利用 jiaba 來做一個簡單過濾器,這個在實際的應(yīng)用中十分常用西土。比如有的詞【的】讶舰,【地】,【得】需了,對數(shù)據(jù)分析沒有什么實際作用跳昼,但是文章中大量的這類詞又會占據(jù)大量的存儲資源,因此我們想要過濾掉這類詞肋乍。
首先建立停用詞表鹅颊,為了便于理解,我們直接建立一個小型的停用詞表墓造。實際中常常需要一個由大量的停用詞組成的詞表堪伍。
stopwords = ('的', '地', '得')
stopwords
自定義待過濾的文本:
string = '我喜歡的和你討厭地以及最不想要得'
string
對 string 進行分詞操作,看看沒過濾之前的分詞結(jié)果觅闽;并將結(jié)果存放在一個 seg_list 中:
seg_list = jieba.cut(string, cut_all=False)
'|'.join(seg_list)
接下來帝雇,查看過濾后結(jié)果。首先創(chuàng)建一個空數(shù)組來存放過濾后的詞語蛉拙,然后通過循環(huán)迭代的方法尸闸,將過濾后的詞語依次添加到剛剛建立的空數(shù)組當(dāng)中。
a = []
seg_list = jieba.cut(string, cut_all=False)
for word in seg_list:
if word not in stopwords:
a.append(word)
a
中文郵件文本分類實戰(zhàn)
文本分類簡介
一般而言刘离,文本分類是指在一定的規(guī)則下室叉,根據(jù)內(nèi)容自動確定文本類別這一過程。文本分類在實際場景中有諸多方面的應(yīng)用硫惕,比如常見的有垃圾郵件分類茧痕,情感分析等,新聞分類等等恼除。
按照分類要求的不同踪旷,文本分類主要可以分為二分類,多分類豁辉,多標(biāo)簽分類三大類令野。
二分類問題:也是最基礎(chǔ)的分類,顧名思義是將文本歸為兩種類別徽级,比如將正常郵件郵件劃分問題气破,垃圾郵件或者正常郵件。一段影評餐抢,判斷是好評還是差評的問題现使。
多分類問題:是將文本劃分為多個類別低匙,比如將新聞歸為政治類,娛樂類碳锈,生活類等等顽冶。
多標(biāo)簽分類:是給文本貼上多個不同的標(biāo)簽,比如一部小說可以同時被劃分為多個主題售碳,可能既是修仙小說强重,又是玄幻小說。
文本分類主要有兩種方法:傳統(tǒng)機器學(xué)習(xí)文本分類算法贸人、深度學(xué)習(xí)文本分類算法间景。
傳統(tǒng)方法:特征提取 + 分類器。就是將文本轉(zhuǎn)換成固定維度的向量灸姊,然后送到分類器中進行分類拱燃。
深度學(xué)習(xí)方法:可以自動提取特征秉溉,實現(xiàn)端到端的訓(xùn)練力惯,有較強的特征表征能力,所以深度學(xué)習(xí)進行文本分類的效果往往要好于傳統(tǒng)的方法召嘶。
支持向量機
SVM 作為傳統(tǒng)機器學(xué)習(xí)的一個非常重要的分類算法父晶,給定訓(xùn)練樣本,支持向量機找到一個劃分超平面弄跌,將不同的類別劃分開來甲喝。通俗來講,這樣的超平面有很多铛只,支持向量機就是要找到位于兩類訓(xùn)練樣本「正中間」的劃分超平面埠胖。為了便于理解,我們用了下面圖中這個簡單的二維空間例子來講解支持向量機的基本原理淳玩,實際應(yīng)用中常常是復(fù)雜的高維空間直撤。
可以看到,圖中有很多的線都可以正確將樣本劃分為兩個類別蜕着,但是紅色的線位于兩個樣本的「正中間」位置谋竖。因為受噪聲和訓(xùn)練集局限性的因素呢,訓(xùn)練集外的樣本可能比訓(xùn)練集的樣本更接近兩個類別的分界承匣,這樣就會導(dǎo)致分類錯誤。恰恰紅色這條線受影響最小,支持向量機的目的就是要找到這條紅線辣往。
圖中紅線為最大分類間隔超平面权埠,虛線上的點是距離分界面最近的點集,這類點就稱為 「支持向量」袍暴。
超平面用線性方程式表示為:
其中
是權(quán)重值些侍,決定了超平面的方向握牧; b 是位移項,決定了超平面與原點之間的距離娩梨。
在空間中沿腰,任意一點xi到超平面的距離 r 公式為:
其中,∣ω∣ 是 ω 的 L2 范數(shù)狈定,L2范數(shù):比如 ω=(a,b,c)颂龙, 那么
當(dāng)且僅當(dāng),點在兩個異類支持向量集合上纽什,分別使等號成立措嵌。此時兩個支持向量之間的「間隔」為:
顯然為了最大化間隔,我們需要找到最小的∣ω∣^ 2芦缰,這就是支持向量機的基本型企巢。
中文郵件分類
垃圾郵件分類任務(wù)整個實驗步驟大致如下:
導(dǎo)入數(shù)據(jù),并進行分詞和剔除停用詞让蕾。
劃分訓(xùn)練集和測試集浪规。
將文本數(shù)據(jù)轉(zhuǎn)化為數(shù)字特征數(shù)據(jù)。
構(gòu)建分類器探孝。
訓(xùn)練分類器笋婿。
測試分類器。
數(shù)據(jù)準(zhǔn)備
本次用到的數(shù)據(jù)包含 3 個文件顿颅, ham_data.txt 文件里面包含 5000 條正常郵件樣本缸濒,spam_data.txt 文件里面包含 5001 個垃圾郵件樣本,stopwords 是停用詞表粱腻。
!wget - nc "http://labfile.oss.aliyuncs.com/courses/1208/ham_data.txt"
!wget - nc "http://labfile.oss.aliyuncs.com/courses/1208/spam_data.txt"
!wget - nc "http://labfile.oss.aliyuncs.com/courses/1208/stop_word.txt"
獲得了樣本之后庇配,首先要做是給正常郵件和垃圾郵件貼上標(biāo)簽,我們用 1 代表正常郵件绍些,0 代表垃圾郵件捞慌。
path1 = 'ham_data.txt' # 正常郵件存放地址
path2 = 'spam_data.txt' # 垃圾郵件地址
用 utf-8 編碼模式打開正常樣本:
h = open(path1, encoding='utf-8')
h
因為我們準(zhǔn)備的數(shù)據(jù)是每一行一封郵件,這里我們要用 readlines() 來以行來讀取文本的內(nèi)容遇革。
h_data = h.readlines()
h_data[0:3] # 顯示前3封正常郵件
同理方式處理垃圾樣本:
s = open(path2, encoding='utf-8')
s_data = s.readlines()
s_data[0:3] # 顯示前3個封垃
讀取之后卿闹,我們的 h_data 數(shù)是由 5000 條郵件字符串組成的正常郵件樣本集, s_data 是由 5001 條郵件字符串組成的垃圾郵件樣本集萝快。下面我們?yōu)閷蓚€樣本組合起來锻霎,并貼上標(biāo)簽,將正常郵件的標(biāo)簽設(shè)置為 1揪漩,垃圾郵件的標(biāo)簽設(shè)置為 0旋恼。
生成一個 len(h_data) 長的的一維全 1 列表:
import numpy as np
h_labels = np.ones(len(h_data)).tolist() # 生成一個len(h_data)長的的一維全1列表
h_labels[0:10] # 我們顯示前10個數(shù)據(jù)
生成一個 len(s_data) 長的的一維全 0 列表:
s_labels = np.zeros(len(s_data)).tolist()
s_labels[0:10] # 我們顯示前10個數(shù)據(jù)
拼接樣本集和標(biāo)簽集:
datas = h_data + s_data # 將正常樣本和垃圾樣本整合到datas當(dāng)中
labels = h_labels + s_labels
這樣我們得到一個由所有郵件樣本組合起來的樣本集 datas 以及一個標(biāo)簽集 labels。
因為我們沒有事先準(zhǔn)備測試集奄容,所以我們在 10001 個樣本當(dāng)中冰更,隨機劃出 25% 個樣本和標(biāo)簽來作為我們的測試集产徊,剩下的 75% 作為訓(xùn)練集來進行訓(xùn)練我們的分類器。這里我們可以用到 scikit-learn 工具里面的 train_test_split 類蜀细。
sklearn.model_selection.train_test_split(datas, labels, test_size=0.25, random_state=5 )
參數(shù)的意義:
datas : 樣本集
labels: 標(biāo)簽集
train_test_split:劃分到測試集的比例
random_state:隨機種子舟铜,取同一個的隨機種子那么每次劃分出的測試集是一樣的。
from sklearn.model_selection import train_test_split
train_d, test_d, train_y, test_y = train_test_split(
datas, labels, test_size=0.25, random_state=5)
train_y[0:30]
分詞
現(xiàn)在對文本進行分詞奠衔,將分詞設(shè)計成 tokenize_words 函數(shù)谆刨,供后續(xù)直接調(diào)用。
import jieba
def tokenize_words(corpus):
tokenized_words = jieba.cut(corpus)
tokenized_words = [token.strip() for token in tokenized_words]
return tokenized_words
驗證一下函數(shù):
string = '我愛自然語言處理'
b = tokenize_words(string)
b
去除停用詞
因為一些字是沒有實際意義的归斤,比如:【的】【了】【得】等痊夭,因此要將其剔除。首先加載我們剛剛下載好的停用詞表脏里。這里也可以自行在網(wǎng)上下載她我,編碼格式為 utf-8,每行一個停用詞迫横。為了方便調(diào)用番舆,我們將去除停用詞的操作放到 remove_stopwords 函數(shù)當(dāng)中。
def remove_stopwords(corpus): # 函數(shù)輸入為樣本集
sw = open('stop_word.txt', encoding='utf-8') # stopwords 停詞表
sw_list = [l.strip() for l in sw] # 去掉文本中的回車符员淫,然后存放到 sw_list 當(dāng)中
# 調(diào)用前面定義好的分詞函數(shù)返回到 tokenized_data 當(dāng)中
tokenized_data = tokenize_words(corpus)
# 過濾停用詞合蔽,對每個在 tokenized_data 中的詞 data 進行判斷
# 如果 data 不在 sw_list 則添加到 filtered_data 當(dāng)中
filtered_data = [data for data in tokenized_data if data not in sw_list]
# 用''將 filtered_data 串起來賦值給 filtered_datas
filtered_datas = ' '.join(filtered_data)
return filtered_datas # 返回去停用詞之后的 datas
構(gòu)建一個函數(shù)完成分詞和剔除停用詞。這里使用 tqdm 模塊顯示進度介返。
from tqdm.notebook import tqdm
def preprocessing_datas(datas):
preprocessed_datas = []
# 對 datas 當(dāng)中的每一個 data 進行去停用詞操作
# 并添加到上面剛剛建立的 preprocessed_datas 當(dāng)中
for data in tqdm(datas):
data = remove_stopwords(data)
preprocessed_datas.append(data)
return preprocessed_datas # 返回去停用詞之后的新的樣本集
用上面預(yù)處理函數(shù)對樣本集進行處理。
pred_train_d = preprocessing_datas(train_d)
pred_train_d[0]
同樣沃斤,對測試集進行預(yù)處理:
pred_test_d = preprocessing_datas(test_d)
pred_test_d[0]
我們得到了分詞過后并且去除停用詞了的樣本集 pred_train_d 和 測試集 pred_test_d圣蝎。
特征提取
在進行分詞及去停用詞處理過后,得到的是一個分詞后的文本『馄浚現(xiàn)在我們的分類器是 SVM徘公,而 SVM 的輸入要求是數(shù)值型的特征。這意味著我們要將前面所進行預(yù)處理的文本數(shù)據(jù)進一步處理哮针,將其轉(zhuǎn)換為數(shù)值型數(shù)據(jù)关面。轉(zhuǎn)換的方法有很多種,為了便于理解十厢,這里使用 TF-IDF 方法等太。為了更好的理解 TF-IDF,我們先從詞袋模型開始講解蛮放。
詞袋模型
詞袋模型是最原始的一類特征集缩抡,忽略掉了文本的語法和語序,用一組無序的單詞序列來表達一段文字或者一個文檔包颁≌跋耄可以這樣理解压真,把整個文檔集的所有出現(xiàn)的詞都丟進袋子里面,然后無序的排出來(去掉重復(fù)的)蘑险。對每一個文檔滴肿,按照詞語出現(xiàn)的次數(shù)來表示文檔。
句子1:我/有/一個/蘋果
句子2:我/明天/去/一個/地方
句子3:你/到/一個/地方
句子4:我/有/我/最愛的/你
把所有詞丟進一個袋子:我佃迄,有嘴高,一個,蘋果和屎,明天拴驮,去,地方柴信,你套啤,到,最愛的随常。這 4 句話中總共出現(xiàn)了這 10 個詞潜沦。
現(xiàn)在我們建立一個無序列表:我,有绪氛,一個唆鸡,蘋果,明天枣察,去争占,地方,你序目,到臂痕,最愛的。并根據(jù)每個句子中詞語出現(xiàn)的次數(shù)來表示每個句子猿涨。
句子 1 特征: ( 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 )
句子 2 特征: ( 1 , 0 , 1 , 0 , 1 , 1 , 1 , 0 , 0 , 0 )
句子 3 特征: ( 0 , 0 , 1 , 0 , 0 , 0 , 1 , 1 , 1 , 0 )
句子 4 特征: ( 2 , 1 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 1 )
這樣的一種特征表示握童,我們就稱之為詞袋模型的特征。
TF-IDF 模型
這種模型主要是用詞匯的統(tǒng)計特征來作為特征集叛赚。TF-IDF 由兩部分組成:TF(Term frequency澡绩,詞頻),IDF(Inverse document frequency俺附,逆文檔頻率)兩部分組成肥卡。
TF:
其中分子nij表示詞i 在文檔j 中出現(xiàn)的頻次。分母則是所有詞頻次的總和昙读,也就是所有詞的個數(shù)召调。
舉個例子:
句子1:上帝/是/一個/女孩
句子2:桌子/上/有/一個/蘋果
句子3:小明/是/老師
句子4:我/有/我/最喜歡/的/
每個句子中詞語的 TF :
IDF:
其中∣D∣ 代表文檔的總數(shù),分母部分∣Di∣ 則是代表文檔集中含有i詞的文檔數(shù)。原始公式是分母沒有+1的唠叛,這里+1是采用了拉普拉斯平滑只嚣,避免了有部分新的詞沒有在語料庫中出現(xiàn)而導(dǎo)致分母為零的情況出現(xiàn)。
用 IDF 計算公式計算句子中每個詞的 IDF 值:
最后艺沼,把 TF 和 IDF 兩個值相乘就可以得到 TF-IDF 的值册舞。即:
每個句子中,詞語的 TF-IDF 值:
把每個句子中每個詞的 TF-IDF 值 添加到向量表示出來就是每個句子的 TF-IDF 特征障般。
句子 1 的特征:
同樣的方法得到句子 2调鲸,3,4 的特征挽荡。
在 Python 當(dāng)中藐石,我們可以通過 scikit-learn 來實現(xiàn) TF-IDF 模型。這里主要用到了 TfidfVectorizer() 類定拟。
sklearn.feature_extraction.text.TfidfVectorizer(min_df=1,norm='l2',smooth_idf=True,use_idf=True,ngram_range=(1,1))
min_df: 忽略掉詞頻嚴格低于定閾值的詞于微。
norm :標(biāo)準(zhǔn)化詞條向量所用的規(guī)范。
smooth_idf:添加一個平滑 IDF 權(quán)重青自,即 IDF 的分母是否使用平滑株依,防止 0 權(quán)重的出現(xiàn)。
use_idf: 啟用 IDF 逆文檔頻率重新加權(quán)延窜。
ngram_range:同詞袋模型
首先加載 TfidfVectorizer 類恋腕,并定義 TF-IDF 模型訓(xùn)練器 vectorizer 。
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(
min_df=1, norm='l2', smooth_idf=True, use_idf=True, ngram_range=(1, 1))
對預(yù)處理過后的 pred_train_d 進行特征提饶嫒稹:
tfidf_train_features = vectorizer.fit_transform(pred_train_d)
tfidf_train_features
通過這一步荠藤,我們得到了 7500 個 28335 維數(shù)的向量作為我們的訓(xùn)練特征集。我們可以查看轉(zhuǎn)換結(jié)果呆万,這里為了便于觀察商源,使用 toarray 方法轉(zhuǎn)換成為數(shù)組數(shù)據(jù)。
tfidf_train_features.toarray()[0]
用訓(xùn)練集訓(xùn)練好特征后的 vectorizer 來提取測試集的特征: 注意這里不能用 vectorizer.fit_transform() 要用 vectorizer.transform()谋减,否則扫沼,將會對測試集單獨訓(xùn)練 TF-IDF 模型,而不是在訓(xùn)練集的詞數(shù)量基礎(chǔ)上做訓(xùn)練缎除。這樣詞總量跟訓(xùn)練集不一樣多,排序也不一樣梢为,將會導(dǎo)致維數(shù)不同,最終無法完成測試铸董。
tfidf_test_features = vectorizer.transform(pred_test_d)
tfidf_test_features
完成之后祟印,我們得到 2501 個 28335 維數(shù)的向量作為我們的測試特征集。
分類
在獲得 TF-IDF 特征之后粟害,我們可以調(diào)用 SGDClassifier() 類來訓(xùn)練 SVM 分類器蕴忆。
sklearn.linear_model.SGDClassifier(loss='hinge')
SGDClassifier 是一個多個分類器的組合,當(dāng)參數(shù) loss='hinge' 時是一個支持向量機分類器悲幅。
加載 SVM 分類器套鹅,并調(diào)整 loss = 'hinge'。
from sklearn.linear_model import SGDClassifier
svm = SGDClassifier(loss='hinge')
然后我們將之前準(zhǔn)備好的樣本集和樣本標(biāo)簽送進 SVM 分類器進行訓(xùn)練汰具。
svm.fit(tfidf_train_features, train_y)
接下來我們用測試集來測試一下分類器的效果卓鹿。
predictions = svm.predict(tfidf_test_features)
predictions
為了直觀顯示分類的結(jié)果,我們用 scikit-learn 庫中的 accuracy_score 函數(shù)來計算一下分類器的準(zhǔn)確率 留荔。
sklearn.metrics.accuracy_score(test_l, prediction)
這個函數(shù)的作用是為了計算 test_l 中與 prediction 相同的比例吟孙。即準(zhǔn)確率。
用測試標(biāo)簽和預(yù)測結(jié)果 計算分類準(zhǔn)確率存谎。np.round(X,2) 的作用是 X 四舍五入后保留小數(shù)點后 2 位數(shù)字拔疚。
from sklearn import metrics
accuracy_score = np.round(metrics.accuracy_score(test_y, predictions), 2)
accuracy_score
隨機提取一個樣本查看其預(yù)測結(jié)果:
print('郵件類型:', test_y[20])
print('預(yù)測郵件類型:', predictions[20])
print('文本:', test_d[20])