文本分類(1)-文本預處理

在進行文本分類之前,需要對文本進行預處理。中文文本和英文文本預處理的方式有所差別。
(1)英文文本預處理
文本預處理過程大致分爲以下幾點:
1、英文縮寫替換
預處理過程中需要把英文縮寫進行替換,如it’s和it is是等價的,won’t和will not也是等價的,等等。

text = "The story loses its bite in a last-minute happy ending that's even less plausible than the rest of the picture ."

text.replace("that's", "that is")

‘The story loses its bite in a last-minute happy ending that is even less plausible than the rest of the picture .’
2、轉換爲小寫字母
文本中英文有大小寫之分,例如"Like"和"like",這兩個字符串是不同的,但在統計單詞時希望把它們認爲是相同的單詞。

text = "This is a film well worth seeing , talking and singing heads and all ."

text.lower()

‘this is a film well worth seeing , talking and singing heads and all .’
3、刪除標點符號、數字及其它特殊字符
標點符號、數字及其它特殊字符對文本分類不起作用,把它們刪除可以減少特徵的維度。一般用正則化方法刪除這些字符。

import re

text = "disney has always been hit-or-miss when bringing beloved kids' books to the screen . . . tuck everlasting is a little of both ."
text = re.sub("[^a-zA-Z]", " ", text)

# 刪除多餘的空格
' '.join(text.split()) 

‘disney has always been hit or miss when bringing beloved kids books to the screen tuck everlasting is a little of both’
4、分詞
英文文本的分詞和中文文本的分詞方法不同,英文文本分詞方法可以根據所提供的文本進行選擇,如果文本中單詞和標點符號或者其它字符是以空格隔開的,例如"a little of both .",那麼可以直接使用split()方法;如果文本中單詞和標點符號沒有用空格隔開,例如"a little of both.",可以使用nltk庫中的word_tokenize()方法。nltk庫安裝也比較簡單,在windows下,用pip install nltk進行安裝即可。

# 單詞和標點符號用空格隔開
text = "part of the charm of satin rouge is that it avoids the obvious with humour and lightness ."
text.split()

[‘part’, ‘of’, ‘the’, ‘charm’, ‘of’, ‘satin’, ‘rouge’, ‘is’, ‘that’, ‘it’, ‘avoids’, ‘the’, ‘obvious’, ‘with’, ‘humour’, ‘and’, ‘lightness’, ‘.’]

# 單詞和標點符號沒有用空格隔開
from nltk.tokenize import word_tokenize

text = "part of the charm of satin rouge is that it avoids the obvious with humour and lightness."
word_tokenize(text)

[‘part’, ‘of’, ‘the’, ‘charm’, ‘of’, ‘satin’, ‘rouge’, ‘is’, ‘that’, ‘it’, ‘avoids’, ‘the’, ‘obvious’, ‘with’, ‘humour’, ‘and’, ‘lightness’, ‘.’]
5、拼寫檢查
由於英文文本中可能有拼寫錯誤,因此一般需要進行拼寫檢查。如果確信分析的文本沒有拼寫錯誤,可以略去此步。拼寫檢查,一般用pyenchant類庫完成。

from enchant.checker import SpellChecker
chkr = SpellChecker("en_US")
chkr.set_text("Many peope likee to watch in the Name of People.")
for err in chkr:
    print("ERROR:", err.word)

ERROR: peope
ERROR: likee
6、詞幹提取和詞形還原
詞幹提取(stemming)和詞型還原(lemmatization)是英文文本預處理的特色。兩者其實有共同點,即都是要找到詞的原始形式。只不過詞幹提取(stemming)會更加激進一點,它在尋找詞幹的時候可以會得到不是詞的詞幹。比如"imaging"的詞幹可能得到的是"imag", 並不是一個詞。而詞形還原則保守一些,它一般只對能夠還原成一個正確的詞的詞進行處理。在nltk中,做詞幹提取的方法有PorterStemmer,LancasterStemmer和SnowballStemmer。推薦使用SnowballStemmer。這個類可以處理很多種語言,當然,除了中文。

from nltk.stem.porter import PorterStemmer
stem_porter = PorterStemmer()
stem_porter.stem('countries')  # 輸出countri
from nltk.stem import SnowballStemmer
stemmer = SnowballStemmer("english")
stemmer.stem("countries")  # 輸出countri
from nltk.stem.wordnet import WordNetLemmatizer
stem_wordnet = WordNetLemmatizer()
stem_wordnet.lemmatize('countries')  # 輸出country

7、刪除停用詞
停用詞對文本分類沒什麼影響,比如’a’,‘is’,‘of’。nltk庫中自帶英文停用詞。

from nltk.corpus import stopwords

stop_words = stopwords.words("english")

text = "part of the charm of satin rouge is that it avoids the obvious with humour and lightness"

words = [w for w in text.split() if w not in stop_words]
' '.join(words)

‘part charm satin rouge avoids obvious humour lightness’
下面我使用傳統的機器學習方法來實現簡單的英文文本分類任務,使用情感分類數據集,是個二分類數據集,標籤爲positive或者negative,數據集可以從網上下載或者發給你。分類任務過程如下:首先進行文本預處理,然後提取TFIDF特徵,最後利用傳統的機器學習方法邏輯迴歸對文本進行分類。我假設文本中沒有拼寫錯誤,另外模型簡單,分類效果一般,需要進一步優化。有什麼不對或者遺漏之處,望各位不吝指教,謝謝。

import numpy as np
import os
import re
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
from nltk.stem.porter import PorterStemmer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score
from sklearn.linear_model import LogisticRegression
import time
import warnings

warnings.filterwarnings('ignore')

path_neg = './polarity_data/rt-polarity.neg'
path_pos = './polarity_data/rt-polarity.pos'

# 文本預處理
def text_preprocessing(path):
    
    # 讀取數據
    text = []
    with open(path, 'r', encoding='utf-8', errors='ignore') as f:
        for line in f.readlines():
            text.append(line.strip())
            
    # 英文縮寫替換
    text_abbreviation = []
    for item in text:
        item = item.lower().replace("it's", "it is").replace("i'm", "i am").replace("he's", "he is").replace("she's", "she is")\
        .replace("we're", "we are").replace("they're", "they are").replace("you're", "you are").replace("that's", "that is")\
        .replace("this's", "this is").replace("can't", "can not").replace("don't", "do not").replace("doesn't", "does not")\
        .replace("we've", "we have").replace("i've", " i have").replace("isn't", "is not").replace("won't", "will not")\
        .replace("hasn't", "has not").replace("wasn't", "was not").replace("weren't", "were not").replace("let's", "let us")
        
        text_abbreviation.append(item)
        
    # 刪除標點符號、數字等其他字符
    text_clear_str = []
    for item in text_abbreviation:
        item = re.sub("[^a-zA-Z]", " ", item)
        text_clear_str.append(' '.join(item.split()))
        
    text_clear_str_stem_del_stopwords = []
    stem_porter = PorterStemmer()  # 詞形歸一化
    stop_words = stopwords.words("english")  # 停用詞

    # 分詞、詞形歸一化、刪除停用詞
    for item in text_clear_str:
        words_token = word_tokenize(item)  # 分詞
        words = [stem_porter.stem(w) for w in words_token if w not in stop_words]
        text_clear_str_stem_del_stopwords.append(' '.join(words))
        
    return text_clear_str_stem_del_stopwords

start_time1 = time.clock()

text_neg = text_preprocessing(path_neg)
text_pos = text_preprocessing(path_pos)

end_time1 = time.clock()

print("the time of text preprocessing is %.2f s" % (end_time1 - start_time1))

text = text_neg + text_pos

# 特徵提取
def features_extraction(text):
    vectors = TfidfVectorizer()
    features = vectors.fit_transform(text).todense()
    return features

start_time2 = time.clock()
features = features_extraction(text)
end_time2 = time.clock()

print("the time of features extracting is %.2f s" % (end_time2 - start_time2))
print('.'*40)

m = len(text_neg)
n = len(text_pos)

# 標籤
labels = np.vstack((np.zeros((m, 1)), np.ones((n, 1))))
# 數據集
data = np.hstack((features, labels))
# 數據集隨機打亂
np.random.shuffle(data)

# 樣本特徵
features = data[:, :-1]
# 樣本標籤
labels = data[:, -1]

print(features.shape)
print(labels.shape)

# 訓練集、測試集劃分
x_train, x_test, y_train, y_test = train_test_split(features, labels, test_size=0.2, random_state=42)

# 邏輯迴歸
start_time3 = time.clock()
lr = LogisticRegression().fit(x_train, y_train)
end_time3 = time.clock()

# 訓練時間
print("the time of LogisicRegression training is %.2f s" % (end_time3 - start_time3))

y_pred = lr.predict(x_test)

print("the f1_score of LogisicRegression is %.4f" % f1_score(y_test, y_pred))

(2)中文文本預處理
1、刪除標點符號、數字及其它特殊字符

import re

text = '最後是12月12日《籃球先鋒報》的新聞報道“湖”塗開營。到底是保羅還是霍華德,\
湖人轉了一圈之後終於發現,三巨頭不切實際,而霍華德也比保羅更符合湖人構建王朝的要求。\
於是,湖人放棄追逐保羅,又把奧多姆送去小牛,目的就是搶魔獸。這一次,他們能如願嗎?標籤:$LOTOzf$'

text = re.sub("[0-9《》“”‘’。、,?!——$¥#@%……&*^()a-zA-Z<>;:/]", "", text)
print(text)

‘最後是月日籃球先鋒報的新聞報道湖塗開營到底是保羅還是霍華德湖人轉了一圈之後終於發現三巨頭不切實際而霍華德也比保羅更符合湖人構建王朝的要求於是湖人放棄追逐保羅又把奧多姆送去小牛目的就是搶魔獸這一次他們能如願嗎標籤’

2、jieba分詞
可使用 jieba.cut 和 jieba.cut_for_search 方法進行分詞,兩者所返回的結構都是一個可迭代的 generator,可使用 for 循環來獲得分詞後得到的每一個詞語(unicode),或者直接使用 jieba.lcut 以及 jieba.lcut_for_search 直接返回 list。其中:jieba.cut 和 jieba.lcut 接受 3 個參數:
sentence:需要分詞的字符串(unicode 或 UTF-8 字符串、GBK 字符串)
cut_all 參數:是否使用全模式,默認值爲 False
HMM 參數:用來控制是否使用 HMM 模型,默認值爲 True

jieba.cut_for_search 和 jieba.lcut_for_search 接受 2 個參數:
sentence:需要分詞的字符串(unicode 或 UTF-8 字符串、GBK 字符串)
HMM 參數:用來控制是否使用 HMM 模型,默認值爲 True
儘量不要使用 GBK 字符串,可能無法預料地錯誤解碼成 UTF-8

jieba 是目前最好的 Python 中文分詞組件,它有以下三種分詞模式:
2.1、精準模式

import jieba

sentence= '他來到北京大學參加暑期夏令營'

# 精準模式
print(list(jieba.cut(sentence, cut_all=False)))

[‘他’, ‘來到’, ‘北京大學’, ‘參加’, ‘暑期’, ‘夏令營’]

2.2、全模式

sentence= '他來到北京大學參加暑期夏令營'

# 全模式
print(list(jieba.cut(sentence, cut_all=True)))

[‘他’, ‘來到’, ‘北京’, ‘北京大學’, ‘大學’, ‘參加’, ‘暑期’, ‘夏令’, ‘夏令營’]

2.3、搜索引擎模式

sentence = '他畢業於北京大學機電系,後來在一機部上海電器科學研究所工作'

# 搜索引擎模式
print(list(jieba.cut_for_search(sentence)))

[‘他’, ‘畢業’, ‘於’, ‘北京’, ‘大學’, ‘北京大學’, ‘機電’, ‘系’, ‘,’, ‘後來’, ‘在’, ‘一機部’, ‘上海’, ‘電器’, ‘科學’, ‘研究’, ‘研究所’, ‘工作’]

3、刪除停用詞
首先需要在網上下載中文的停用詞表或者自己製作停用詞表,也可以下載好停用詞表,然後加上一些其它的停用詞。

import jieba
import re

# 停用詞
stop_words = ['於', '後來', '在']

sentence = '他畢業於北京大學機電系後來在一機部上海電器科學研究所工作'

# 分詞
sent_cut = list(jieba.cut(sentence))

# 刪除停用詞
text = [w for w in sent_cut if w not in stop_words]
    
print('分詞:', sent_cut)
print('刪除停用詞:', text)

分詞: [‘他’, ‘畢業’, ‘於’, ‘北京大學’, ‘機電’, ‘系’, ‘後來’, ‘在’, ‘一機部’, ‘上海’, ‘電器’, ‘科學’, ‘研究所’, ‘工作’]
刪除停用詞: [‘畢業’, ‘北京大學’, ‘機電’, ‘系’, ‘一機部’, ‘上海’, ‘電器’, ‘科學’, ‘研究所’, ‘工作’]
詳細代碼可以在我的github上查看https://github.com/LeungFe/Text-preprocessing

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章