逻辑回归实现文本分类
基于sklearn的文本分類—邏輯回歸
本文是文本分類的第一篇,記錄使用邏輯回歸進行文本分類任務,數據集下載地址:http://thuctc.thunlp.org/
文本分類的主要內容如下:?
- 1.基于邏輯回歸的文本分類?
- 2.基于樸素貝葉斯的文本分類?
- 3.使用LDA進行文檔降維以及特征選擇?
- 4.基于SVM的文本分類?
- 5.基于多層感知機MLPC的文本分類?
- 6.基于卷積神經網絡詞級別的文本分類以及調參?
- 7.基于卷積神經網絡的句子級別的文本分類以及調參?
- 8.基于Facebook fastText的快速高效文本分類?
- 9.基于RNN的文本分類?
- 10.基于LSTM的文本分類?
- 11.總結
1 數據預處理
其中使用的訓練數據來自清華大學開源的文本分類數據集,原始數據集比較大,提供下載的是提取的小數據,thu_data_500 表示每個類提取500篇文章,thu_data_3000 表示每個類提取3000篇文章,一共14個類別,數據處理的代碼如下:
import os import codecs import jieba import refrom sklearn.utils import shuffle category = ['星座', '股票', '房產', '時尚', '體育', '社會', '家居', '游戲', '彩票', '科技', '教育', '時政', '娛樂', '財經']# 每篇文檔保留的文檔數量 #per_class_max_docs = 1000def load_data_to_mini(path, to_path, per_class_max_docs=1000):"""處理清華大學語料庫,將類別和文檔處理成fasttext 所需要的格式:param path: :param to_path: :return: """# 抽取后的語料庫corpus = []if not os.path.isdir(path):print('path error')# 列舉當前目錄下的所有子列別目錄with codecs.open(to_path, 'w') as f:for files in os.listdir(path):curr_path = os.path.join(path, files)print(curr_path)if os.path.isdir(curr_path):count = 0docs = []for file in os.listdir(curr_path):count += 1if count > per_class_max_docs:breakfile_path = os.path.join(curr_path, file)# 讀取文件中的內容with codecs.open(file_path, 'r', encoding='utf-8') as fd:docs.append('__label__' + files + ' ' + ' '.join(jieba.cut(re.sub('[ \n\r\t]+', '', fd.read()))))f.write('__label__' + files + ' ' + ' '.join(jieba.cut(re.sub('[ \n\r\t]+', '', fd.read()))))corpus.append(docs)# 將數據寫到一個新的文件中with codecs.open(to_path, 'a') as f:for docs in corpus:for doc in docs:f.write(doc + '\n')return corpus
通過調用下面的代碼,執行小數據集的提取
corpus = load_data_to_mini('/root/git/data/THUCNews', 'thu_data_all', 1000)我們看下提取的結果
print('corpus size(%d,%d)' %(len(corpus), len(corpus[0]))) corpus size(14,1000)可以看到,結果一共是14個類,每個類1000篇文檔,下面看下corpus里面的具體內容
corpus[0][1] '__label__股票 世基 投資 : 緊縮 壓力 驟然 增加 滬 指 再失 2800 \u3000 \u3000 余煒 \u3000 \u3000 周二 大盤 在 半年線 處 止跌 后 , 連續 3 日 展開 反彈 , 昨日 一度 站上 過 2830 點 , 但 最終 還是 未能 收復 , 顯示 出 20 日線 和 年線 從技術上 對 市場 的 壓力 比 想象 中 更加 大 。 弱勢 格局 下 , 利空 傳言 紛至沓來 , 周末 效應 再次 顯現 , 周五 大盤 給 我們 呈現出 的 是 一幕 疲軟 下滑 走勢 , 使得 前三天 的 反彈 基本 化為烏有 , 2800 點 再 一次 失守 , 股指 重新 來到 半年線 附近 求 支撐 。 \u3000 \u3000 盤面 熱點 比較 稀少 , 其中 資產重組 、 業績 增長 和 預增 題材 的 幾只 品種 漲勢 不錯 , 昨日 提到 過 的 廣電 信息 、 銀鴿投資 連續 漲停 , 計劃 高送 轉 的 精誠 銅業 也 受大單 推動 漲停 。 此外 , 高鐵 概念 逆勢 重新 活躍 , 晉億 實業 最高 逼近 漲停 , 帶動 晉西 車軸 、 中國 北車 、 天馬 股份 等 快速 上攻 , 其中 北車 和 晉 億 實業 已經 率先 創出 階段 新高 。 部分 具備 病菌 概念 的 醫藥 股 也 表現 較 好 , 自早 盤起 就 展現 強勢 , 萊茵 生物 漲停 , 紫鑫 藥業 、 海王 生物 、 聯環 藥業 大漲 7% 左右 。 \u3000 \u3000 從 目前 公開 消息 來看 , 相信 是 貨幣政策 面 的 利空 預期 在 對 市場 形成 壓力 , 隨著 韓國 央行 的 昨日 加息 , 新興 經濟體 緊縮 預期 驟然 升溫 , 之前 秘魯 和 泰國 已經 有過 連續 加息 的 動作 , 與此同時 , 雖然 西方 主要 國家 仍 在 維持 寬松 , 但 隨著 發達 經濟體 復蘇 步伐 的 加快 , 通脹 也 有 抬頭 跡象 。 國內 方面 , 下周 可能 將 公布 2010 年 全年 和 12 月份 的 經濟運行 數據 , 形勢 擺在 這里 , 投資者 有所 擔憂 也 是 情理之中 。 歡迎 發表 評論 \xa0 \xa0 我要 評論' 可以看到,開頭時 label 的本文標簽,后面接著的是新聞正文,正文已經使用jieba進行了分詞,詞之間使用空格鍵分開。?下面進行數據的切分,將數據劃分為樣本和標簽,因為讀取的數據是按照類別來分塊的,在后面采用訓練數據和測試數據的時候,會出現問題,所以這里也需要進行數據的隨機打亂,數據打亂最好不要使用numpy.random.shuffle(),這個效率很低,而且非常容易出現內存溢出問題,推薦使用的是pandas或者是sklearn中的shuffle,我使用的是后者。切分的代碼如下:
def split_data_with_label(corpus):"""將數據劃分為訓練數據和樣本標簽:param corpus: :return: """input_x = []input_y = []tag = []if os.path.isfile(corpus):with codecs.open(corpus, 'r') as f:for line in f:tag.append(line)else:for docs in corpus:for doc in docs:tag.append(doc)tag = shuffle(tag)for doc in tag:index = doc.find(' ')input_y.append(doc[:index])input_x.append(doc[index + 1 :])# 打亂數據,避免在采樣的時候出現類別不均衡現象# datasets = np.column_stack([input_x, input_y])# np.random.shuffle(datasets)# input_x = []# input_y = []# for i in datasets:# input_x.append(i[:-1])# input_y.append(i[-1:])return [input_x, input_y]這個函數返回兩個值,其中第一個返回值input_x是樣本數據,一共14*1000行,第二個參數input_y和input_x有著相同的行數,每行對應著input_x中新聞樣本的類別標簽.
2.特征選擇
下面將進行特征提取,特征選擇的方法有基本的bag-of-words, tf-idf,n-gran等,我們主要使用TF-IDF進行這些方法進行實驗,下面是代碼:
from sklearn.feature_extraction.text import CountVectorizer from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.cross_validation import train_test_split from sklearn.metrics.scorer import make_scorer from sklearn import linear_model from sklearn import metricsfrom time import time/usr/local/lib/python3.5/dist-packages/sklearn/cross_validation.py:41: DeprecationWarning: This module was deprecated in version 0.18 in favor of the model_selection module into which all the refactored classes and functions are moved. Also note that the interface of the new CV iterators are different from that of this module. This module will be removed in 0.20."This module will be removed in 0.20.", DeprecationWarning) def feature_extractor(input_x, case='tfidf', max_df=1.0, min_df=0.0):"""特征抽取:param corpus: :param case: 不同的特征抽取方法:return: """return TfidfVectorizer(token_pattern='\w', ngram_range=(1,2), max_df=max_df, min_df=min_df).fit_transform(input_x)
接下來將進行訓練數據和測試數據的切分,現在不進行更好的交叉驗證等技術,僅僅簡單的以一定的比例劃分訓練數據和測試數據。使用sklearn中提供的工具,具體代碼如下:
def split_data_to_train_and_test(corpus, indices=0.2, random_state=10, shuffle=True):"""將數據劃分為訓練數據和測試數據:param corpus: [input_x]:param indices: 劃分比例:random_state: 隨機種子:param shuffle: 是否打亂數據:return: """input_x, y = corpus# 切分數據集x_train, x_dev, y_train, y_dev = train_test_split(input_x, y, test_size=indices, random_state=10)print("Vocabulary Size: {:d}".format(input_x.shape[1]))print("Train/Dev split: {:d}/{:d}".format(len(y_train), len(y_dev)))return x_train, x_dev, y_train, y_dev函數返回四個值,分別是訓練數據的樣本,訓練數據的標簽,測試數據樣本,測試數據真實標簽,下面調用樸素貝葉斯進行分類。
邏輯回歸是一種判別式模型,在線性回歸的基礎上,套用了一個sigmod函數,這個函數講線性結果映射到一個概率區間,并且概率在0.5周圍是光滑的,這就使得數據的分類結果都趨向于在0,1這兩端。
LogisticRegression()主要有的參數:?
- penalty: 表示正則項為L1或者L2,默認是L2?
- C 正則項的參數C,也就是懲罰系數?
- fit_intercept 表示線性模型中的bias,也就是模型中的參數b 是一個布爾值,表示帶或者不帶bias,一般都是帶的!?
- solver 表示的是參數學習的方法,有{‘newton-cg’, ‘lbfgs’, ‘liblinear’, ‘sag’, ‘saga’}這幾種情況,在小數據集下liblinear是相對好的選擇,在數據集大的時候,選擇saga,sag會是一個更好的選擇,但是liblinear在多分類情況下受限制與ovr,所以其他的選擇在多分類下更明智
這里主要是進行相關的實驗,不在理論上展開太多,下面采用邏輯回歸進行文檔分類,具體代碼如下:
def fit_and_predicted(train_x, train_y, test_x, test_y, penalty='l2', C=1.0, solver='lbfgs'):"""訓練與預測:param train_x: :param train_y: :param test_x: :param test_y: :return: """clf = linear_model.LogisticRegression(penalty=penalty, C=C, solver=solver, n_jobs=-1).fit(train_x, train_y)predicted = clf.predict(test_x)print(metrics.classification_report(test_y, predicted))print('accuracy_score: %0.5fs' %(metrics.accuracy_score(test_y, predicted)))上面函數調用LogisticRegression(),基于線性分類器的變種模型有很多,我們將會在后面的試驗中使用添加了L1范式的lasso回歸,添加了L2范式的嶺回歸回歸
下面將進行實際的代碼運行階段了。
# 1. 加載語料 corpus = split_data_with_label('thu_data_1000')2.1 TF-IDF (max_df, min_df)=dafault (1.0,0.0)
input_x, y = corpus # 2. 特征選擇 input_x = feature_extractor(input_x, 'tfidf') # 3.切分訓練數據和測試數據 train_x, test_x, train_y, test_y = split_data_to_train_and_test([input_x, y]) Vocabulary Size: 942969 Train/Dev split: 11200/2800 # 4. 訓練以及測試 t0 = time() print('\t\t使用 max_df,min_df=(1.0,0.0) 進行特征選擇的邏輯回歸文本分類\t\t') fit_and_predicted(train_x, train_y, test_x, test_y) print('time uesed: %0.4fs' %(time() - t0)) 使用 max_df,min_df=(1.0,0.0) 進行特征選擇的邏輯回歸文本分類 precision recall f1-score support__label__體育 0.93 0.97 0.95 186 __label__娛樂 0.87 0.88 0.88 233 __label__家居 0.88 0.90 0.89 203 __label__彩票 0.98 0.96 0.97 207 __label__房產 0.90 0.90 0.90 178 __label__教育 0.92 0.91 0.92 208 __label__時尚 0.92 0.92 0.92 197 __label__時政 0.83 0.87 0.85 211 __label__星座 0.94 0.97 0.95 202 __label__游戲 0.96 0.91 0.94 202 __label__社會 0.85 0.92 0.88 210 __label__科技 0.88 0.80 0.84 173 __label__股票 0.87 0.82 0.84 196 __label__財經 0.93 0.90 0.91 194avg / total 0.90 0.90 0.90 2800accuracy_score: 0.90321s time uesed: 44.1572s可以看出邏輯回歸的文本分類方法在該數據集上表現良好,綜合得分都有91%以上,下面我們將對tf-idf做文章,看看不同的tf-idf參數對特征產生影響
2.2 TF-IDF 不同的max_df對結果參數的影響
# 2. 特征選擇 max_df = [0.2, 0.4, 0.5, 0.8, 1.0, 1.5, 5] for i in max_df:input_x, y = corpusinput_x = feature_extractor(input_x, 'tfidf', max_df=i)# 3.切分訓練數據和測試數據train_x, test_x, train_y, test_y = split_data_to_train_and_test([input_x, y])# 4. 訓練以及測試t0 = time()print('\t 使用 max_df,min_df=(%.1f,0.0) 進行特征選擇的邏輯回歸文本分類\t\t\n' %(i))fit_and_predicted(train_x, train_y, test_x, test_y)print('time uesed: %0.4fs' %(time() - t0)) Vocabulary Size: 670795 Train/Dev split: 5600/1400使用 max_df,min_df=(0.2,0.0) 進行特征選擇的邏輯回歸文本分類 precision recall f1-score support_體育_ 0.95 0.95 0.95 109_娛樂_ 0.73 0.88 0.80 92_家居_ 0.98 0.88 0.93 109_彩票_ 0.99 0.96 0.97 97_房產_ 0.94 0.94 0.94 97_教育_ 0.94 0.89 0.92 104_時尚_ 0.87 0.87 0.87 110_時政_ 0.86 0.89 0.87 93_星座_ 0.96 0.93 0.95 105_游戲_ 0.97 0.91 0.94 103_社會_ 0.84 0.88 0.86 99_科技_ 0.85 0.86 0.86 93_股票_ 0.82 0.88 0.85 78_財經_ 0.97 0.91 0.94 111avg / total 0.91 0.90 0.91 1400accuracy_score: 0.90429s time uesed: 63.8214s Vocabulary Size: 671113 Train/Dev split: 5600/1400使用 max_df,min_df=(0.4,0.0) 進行特征選擇的邏輯回歸文本分類 precision recall f1-score support_體育_ 0.97 0.94 0.96 109_娛樂_ 0.78 0.89 0.83 92_家居_ 0.94 0.87 0.90 109_彩票_ 0.98 0.97 0.97 97_房產_ 0.93 0.92 0.92 97_教育_ 0.95 0.88 0.92 104_時尚_ 0.90 0.86 0.88 110_時政_ 0.83 0.90 0.87 93_星座_ 0.94 0.95 0.95 105_游戲_ 0.95 0.90 0.93 103_社會_ 0.86 0.90 0.88 99_科技_ 0.88 0.87 0.88 93_股票_ 0.80 0.91 0.85 78_財經_ 0.95 0.89 0.92 111avg / total 0.91 0.91 0.91 1400accuracy_score: 0.90500s time uesed: 62.6203s Vocabulary Size: 671183 Train/Dev split: 5600/1400使用 max_df,min_df=(0.5,0.0) 進行特征選擇的邏輯回歸文本分類 precision recall f1-score support_體育_ 0.97 0.94 0.96 109_娛樂_ 0.79 0.89 0.84 92_家居_ 0.93 0.87 0.90 109_彩票_ 0.98 0.97 0.97 97_房產_ 0.93 0.91 0.92 97_教育_ 0.94 0.88 0.91 104_時尚_ 0.91 0.87 0.89 110_時政_ 0.83 0.89 0.86 93_星座_ 0.94 0.96 0.95 105_游戲_ 0.95 0.89 0.92 103_社會_ 0.85 0.90 0.87 99_科技_ 0.88 0.87 0.88 93_股票_ 0.80 0.91 0.85 78_財經_ 0.94 0.88 0.91 111avg / total 0.91 0.90 0.90 1400accuracy_score: 0.90357s time uesed: 63.2442s Vocabulary Size: 671261 Train/Dev split: 5600/1400使用 max_df,min_df=(0.8,0.0) 進行特征選擇的邏輯回歸文本分類 precision recall f1-score support_體育_ 0.96 0.94 0.95 109_娛樂_ 0.79 0.88 0.83 92_家居_ 0.92 0.89 0.91 109_彩票_ 0.98 0.97 0.97 97_房產_ 0.93 0.91 0.92 97_教育_ 0.95 0.88 0.92 104_時尚_ 0.91 0.87 0.89 110_時政_ 0.83 0.88 0.85 93_星座_ 0.94 0.97 0.96 105_游戲_ 0.95 0.89 0.92 103_社會_ 0.85 0.89 0.87 99_科技_ 0.88 0.85 0.86 93_股票_ 0.77 0.91 0.84 78_財經_ 0.95 0.88 0.92 111avg / total 0.91 0.90 0.90 1400accuracy_score: 0.90143s time uesed: 64.9945s Vocabulary Size: 671267 Train/Dev split: 5600/1400使用 max_df,min_df=(1.0,0.0) 進行特征選擇的邏輯回歸文本分類 precision recall f1-score support_體育_ 0.95 0.94 0.94 109_娛樂_ 0.80 0.88 0.84 92_家居_ 0.95 0.89 0.92 109_彩票_ 0.98 0.95 0.96 97_房產_ 0.93 0.92 0.92 97_教育_ 0.95 0.88 0.92 104_時尚_ 0.91 0.87 0.89 110_時政_ 0.81 0.90 0.85 93_星座_ 0.94 0.98 0.96 105_游戲_ 0.95 0.90 0.93 103_社會_ 0.84 0.89 0.86 99_科技_ 0.89 0.84 0.86 93_股票_ 0.76 0.91 0.83 78_財經_ 0.96 0.87 0.92 111avg / total 0.91 0.90 0.90 1400accuracy_score: 0.90214s time uesed: 67.9015s Vocabulary Size: 671267 Train/Dev split: 5600/1400使用 max_df,min_df=(1.5,0.0) 進行特征選擇的邏輯回歸文本分類 precision recall f1-score support_體育_ 0.95 0.94 0.94 109_娛樂_ 0.80 0.88 0.84 92_家居_ 0.95 0.89 0.92 109_彩票_ 0.98 0.95 0.96 97_房產_ 0.93 0.92 0.92 97_教育_ 0.95 0.88 0.92 104_時尚_ 0.91 0.87 0.89 110_時政_ 0.81 0.90 0.85 93_星座_ 0.94 0.98 0.96 105_游戲_ 0.95 0.90 0.93 103_社會_ 0.84 0.89 0.86 99_科技_ 0.89 0.84 0.86 93_股票_ 0.76 0.91 0.83 78_財經_ 0.96 0.87 0.92 111avg / total 0.91 0.90 0.90 1400accuracy_score: 0.90214s time uesed: 66.4803s Vocabulary Size: 562057 Train/Dev split: 5600/1400使用 max_df,min_df=(5.0,0.0) 進行特征選擇的邏輯回歸文本分類 precision recall f1-score support_體育_ 0.94 0.83 0.88 109_娛樂_ 0.61 0.75 0.67 92_家居_ 0.91 0.55 0.69 109_彩票_ 0.95 0.84 0.89 97_房產_ 0.94 0.81 0.87 97_教育_ 0.92 0.80 0.86 104_時尚_ 0.82 0.72 0.77 110_時政_ 0.77 0.78 0.78 93_星座_ 0.86 0.75 0.80 105_游戲_ 0.96 0.72 0.82 103_社會_ 0.77 0.78 0.77 99_科技_ 0.64 0.81 0.71 93_股票_ 0.33 0.88 0.48 78_財經_ 0.97 0.67 0.79 111avg / total 0.83 0.76 0.78 1400accuracy_score: 0.75929s time uesed: 22.6883s
從實驗結果可以看出,最好的max_df是在0.4-0.5之間,這也就是為什么很多demo中設置TF-IDF閾值進行特征的篩選,下面設置在max_df為1.0的目標下測試min_df;
2.2 TF-IDF 不同的min_df對結果參數的影響
# 2. 特征選擇 min_df = [0., 0.1, 0.2, 0.3, 0.4] for i in min_df:input_x, y = corpusinput_x = feature_extractor(input_x, 'tfidf', max_df=1.0, min_df=i)# 3.切分訓練數據和測試數據train_x, test_x, train_y, test_y = split_data_to_train_and_test([input_x, y])# 4. 訓練以及測試t0 = time()print('\t 使用 max_df,min_df=(1.0,%.1f) 進行特征選擇的邏輯回歸文本分類\t\t\n' %(i))fit_and_predicted(train_x, train_y, test_x, test_y)print('time uesed: %0.4fs' %(time() - t0)) Vocabulary Size: 671267 Train/Dev split: 5600/1400使用 max_df,min_df=(1.0,0.0) 進行特征選擇的邏輯回歸文本分類 precision recall f1-score support_體育_ 0.95 0.94 0.94 109_娛樂_ 0.80 0.88 0.84 92_家居_ 0.95 0.89 0.92 109_彩票_ 0.98 0.95 0.96 97_房產_ 0.93 0.92 0.92 97_教育_ 0.95 0.88 0.92 104_時尚_ 0.91 0.87 0.89 110_時政_ 0.81 0.90 0.85 93_星座_ 0.94 0.98 0.96 105_游戲_ 0.95 0.90 0.93 103_社會_ 0.84 0.89 0.86 99_科技_ 0.89 0.84 0.86 93_股票_ 0.76 0.91 0.83 78_財經_ 0.96 0.87 0.92 111avg / total 0.91 0.90 0.90 1400accuracy_score: 0.90214s time uesed: 67.8985s Vocabulary Size: 1052 Train/Dev split: 5600/1400使用 max_df,min_df=(1.0,0.1) 進行特征選擇的邏輯回歸文本分類 precision recall f1-score support_體育_ 0.93 0.94 0.94 109_娛樂_ 0.75 0.75 0.75 92_家居_ 0.87 0.85 0.86 109_彩票_ 0.97 0.97 0.97 97_房產_ 0.91 0.88 0.89 97_教育_ 0.95 0.88 0.92 104_時尚_ 0.88 0.84 0.86 110_時政_ 0.82 0.89 0.86 93_星座_ 0.93 0.96 0.94 105_游戲_ 0.95 0.88 0.91 103_社會_ 0.84 0.88 0.86 99_科技_ 0.79 0.78 0.79 93_股票_ 0.73 0.90 0.80 78_財經_ 0.94 0.86 0.90 111avg / total 0.88 0.88 0.88 1400accuracy_score: 0.87786s time uesed: 1.8236s Vocabulary Size: 472 Train/Dev split: 5600/1400使用 max_df,min_df=(1.0,0.2) 進行特征選擇的邏輯回歸文本分類 precision recall f1-score support_體育_ 0.89 0.92 0.90 109_娛樂_ 0.71 0.79 0.75 92_家居_ 0.80 0.79 0.79 109_彩票_ 0.97 0.86 0.91 97_房產_ 0.92 0.80 0.86 97_教育_ 0.96 0.84 0.89 104_時尚_ 0.86 0.82 0.84 110_時政_ 0.77 0.84 0.80 93_星座_ 0.85 0.90 0.88 105_游戲_ 0.78 0.66 0.72 103_社會_ 0.77 0.87 0.82 99_科技_ 0.71 0.73 0.72 93_股票_ 0.65 0.83 0.73 78_財經_ 0.92 0.85 0.88 111avg / total 0.83 0.82 0.82 1400accuracy_score: 0.82214s time uesed: 1.4046s Vocabulary Size: 244 Train/Dev split: 5600/1400使用 max_df,min_df=(1.0,0.3) 進行特征選擇的邏輯回歸文本分類 precision recall f1-score support_體育_ 0.87 0.89 0.88 109_娛樂_ 0.73 0.66 0.70 92_家居_ 0.81 0.74 0.78 109_彩票_ 0.90 0.84 0.87 97_房產_ 0.90 0.80 0.85 97_教育_ 0.84 0.78 0.81 104_時尚_ 0.78 0.83 0.81 110_時政_ 0.68 0.78 0.73 93_星座_ 0.83 0.86 0.84 105_游戲_ 0.75 0.61 0.67 103_社會_ 0.70 0.84 0.76 99_科技_ 0.69 0.72 0.71 93_股票_ 0.60 0.78 0.68 78_財經_ 0.87 0.78 0.82 111avg / total 0.79 0.78 0.78 1400accuracy_score: 0.78143s time uesed: 1.1189s Vocabulary Size: 154 Train/Dev split: 5600/1400使用 max_df,min_df=(1.0,0.4) 進行特征選擇的邏輯回歸文本分類 precision recall f1-score support_體育_ 0.82 0.83 0.82 109_娛樂_ 0.66 0.63 0.64 92_家居_ 0.75 0.60 0.66 109_彩票_ 0.91 0.85 0.88 97_房產_ 0.82 0.75 0.78 97_教育_ 0.77 0.72 0.74 104_時尚_ 0.70 0.75 0.73 110_時政_ 0.65 0.78 0.71 93_星座_ 0.85 0.86 0.85 105_游戲_ 0.63 0.57 0.60 103_社會_ 0.70 0.76 0.73 99_科技_ 0.61 0.67 0.64 93_股票_ 0.54 0.67 0.60 78_財經_ 0.78 0.72 0.75 111avg / total 0.73 0.73 0.73 1400accuracy_score: 0.72643s time uesed: 1.0229s
從上面的實驗可以看出,min_df或許取0.0是一個不錯的選擇,那就默認吧。
不得不說,總感覺上面的控制變量進行參數的尋找是有毛病的,暫且就這么做吧。在后面的試驗中,我們將選取max_df=0.5,min_df=0.0進行相關實驗。
3 邏輯回歸的調參
3.1 交叉驗證
在進行最優參數的調整之前,我們先看一下sklearn提供的另外一個函數LogisticRegressionCV(),它提供了標準的k-fold-cross-validator
def fit_and_predicted_use_CV(train_x, train_y, test_x, test_y, penalty='l2', C=1.0, solver='lbfgs', cv=10):"""訓練與預測:param train_x: :param train_y: :param test_x: :param test_y: :return: """clf = linear_model.LogisticRegressionCV(penalty=penalty, C=C, solver=solver, n_jobs=-1, cv=cv).fit(train_x, train_y)predicted = clf.predict(test_x)print(metrics.classification_report(test_y, predicted))print('accuracy_score: %0.5fs' %(metrics.accuracy_score(test_y, predicted))) input_x, y = corpus input_x = feature_extractor(input_x, 'tfidf', max_df=i) # 3.切分訓練數據和測試數據 train_x, test_x, train_y, test_y = split_data_to_train_and_test([input_x, y]) # 4. 訓練以及測試 t0 = time() print('\t 使用 max_df,min_df=(%.1f,0.0) 進行特征選擇的邏輯回歸文本分類\t\t\n' %(i)) fit_and_predicted_use_CV(train_x, train_y, test_x, test_y) print('time uesed: %0.4fs' %(time() - t0))可以看到使用交叉驗證的結果往往比我們直接劃分數據集的效果要好一些。
3.2 邏輯回歸的最佳參數尋找
現在將采用sklearn中提供的網格查找方法進行最優參數的尋找,網格查找其實是一種暴力查找方法。
import numpy as np from sklearn.grid_search import GridSearchCV def train_and_predicted_with_graid(corpus, param_grid, cv=5):input_x, y = corpusscoring = ['precision_macro', 'recall_macro', 'f1_macro']clf = linear_model.LogisticRegression(n_jobs=-1)grid = GridSearchCV(clf, param_grid, cv=cv, scoring='accuracy')scores = grid.fit(input_x, y)print('parameters:')best_parameters = grid.best_estimator_.get_params()for param_name in sorted(best_parameters):print('\t%s: %r' %(param_name, best_parameters[param_name]))return scores C= [0.1, 0.2, 0.5, 0.8, 1.5, 3, 5] fit_intercept=[True, False] penalty=['l1', 'l2'] solver=['newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga'] solver = ['saga'] param_grid=dict(C=C, fit_intercept=fit_intercept, penalty=penalty, solver=solver) input_x, y = corpus input_x = feature_extractor(input_x, 'tfidf', max_df=0.5, min_df=0.0) scores = train_and_predicted_with_graid([input_x, y], cv=5, param_grid=param_grid) parameters:C: 5class_weight: Nonedual: Falsefit_intercept: Trueintercept_scaling: 1max_iter: 100multi_class: 'ovr'n_jobs: -1penalty: 'l2'random_state: Nonesolver: 'saga'tol: 0.0001verbose: 0warm_start: False4. 其他線性分類器
4.1 簡單的線性回歸
def fit_and_predicted_with_linerCV(train_x, train_y, test_x, test_y, alpha=1.0, cv=10):"""訓練與預測:param train_x: :param train_y: :param test_x: :param test_y: :return: """clf = linear_model.LogisticRegressionCV(penalty=penalty, C=C, solver=solver, n_jobs=-1, cv=cv).fit(train_x, train_y)predicted = clf.predict(test_x)print(metrics.classification_report(test_y, predicted))print('accuracy_score: %0.5fs' %(metrics.accuracy_score(test_y, predicted))) input_x, y = corpus input_x = feature_extractor(input_x, 'tfidf', max_df=i) # 3.切分訓練數據和測試數據 train_x, test_x, train_y, test_y = split_data_to_train_and_test([input_x, y]) # 4. 訓練以及測試 t0 = time() print('\t 使用線性回歸的文本分類\t\t\n' %(i)) fit_and_predicted_with_linerCV(train_x, train_y, test_x, test_y) print('time uesed: %0.4fs' %(time() - t0)) {'fit_time': array([ 0.69856882, 0.6891861 , 0.68457079, 0.68122745, 0.68401599]),'score_time': array([ 0.24055672, 0.25055385, 0.24642444, 0.24583435, 0.25062966]),'test_f1_macro': array([ 0.93190598, 0.93358814, 0.92900074, 0.93620104, 0.93139325]),'test_precision_macro': array([ 0.93411186, 0.93509947, 0.93082131, 0.93790787, 0.93312355]),'test_recall_macro': array([ 0.93178571, 0.93357143, 0.92892857, 0.93607143, 0.93142857]),'train_f1_macro': array([ 0.95534592, 0.95516529, 0.95665886, 0.95573948, 0.95629695]),'train_precision_macro': array([ 0.95629235, 0.95618146, 0.95767379, 0.9566414 , 0.95725075]),'train_recall_macro': array([ 0.95526786, 0.95508929, 0.95660714, 0.95571429, 0.95625 ])}4.2 使用L1范式的Lasso
def fit_and_predicted_with_LassoCV(train_x, train_y, test_x, test_y):"""訓練與預測:param train_x: :param train_y: :param test_x: :param test_y: :return: """clf = linear_model.LassoCV().fit(train_x, train_y)predicted = clf.predict(test_x)print(metrics.classification_report(test_y, predicted))print('accuracy_score: %0.5fs' %(metrics.accuracy_score(test_y, predicted))) input_x, y = corpus input_x = feature_extractor(input_x, 'tfidf', max_df=i) # 3.切分訓練數據和測試數據 train_x, test_x, train_y, test_y = split_data_to_train_and_test([input_x, y]) # 4. 訓練以及測試 t0 = time() print('\t 使用線性回歸的文本分類\t\t\n' %(i)) fit_and_predicted_with_LassoCV(train_x, train_y, test_x, test_y) print('time uesed: %0.4fs' %(time() - t0)) {'fit_time': array([ 0.69856882, 0.6891861 , 0.68457079, 0.68122745, 0.68401599]),'score_time': array([ 0.24055672, 0.25055385, 0.24642444, 0.24583435, 0.25062966]),'test_f1_macro': array([ 0.93190598, 0.93358814, 0.92900074, 0.93620104, 0.93139325]),'test_precision_macro': array([ 0.93411186, 0.93509947, 0.93082131, 0.93790787, 0.93312355]),'test_recall_macro': array([ 0.93178571, 0.93357143, 0.92892857, 0.93607143, 0.93142857]),'train_f1_macro': array([ 0.95534592, 0.95516529, 0.95665886, 0.95573948, 0.95629695]),'train_precision_macro': array([ 0.95629235, 0.95618146, 0.95767379, 0.9566414 , 0.95725075]),'train_recall_macro': array([ 0.95526786, 0.95508929, 0.95660714, 0.95571429, 0.95625 ])}4.3 使用L2范式的嶺回歸
def fit_and_predicted_with_RidgeCV(train_x, train_y, test_x, test_y):"""訓練與預測:param train_x: :param train_y: :param test_x: :param test_y: :return: """clf = linear_model.RidgeClassifierCV().fit(train_x, train_y)predicted = clf.predict(test_x)print(metrics.classification_report(test_y, predicted))print('accuracy_score: %0.5fs' %(metrics.accuracy_score(test_y, predicted))) input_x, y = corpus input_x = feature_extractor(input_x, 'tfidf', max_df=i) # 3.切分訓練數據和測試數據 train_x, test_x, train_y, test_y = split_data_to_train_and_test([input_x, y]) # 4. 訓練以及測試 t0 = time() print('\t 使用線性回歸的文本分類\t\t\n' %(i)) fit_and_predicted_with_RidgeCV(train_x, train_y, test_x, test_y) print('time uesed: %0.4fs' %(time() - t0)) {'fit_time': array([ 0.69856882, 0.6891861 , 0.68457079, 0.68122745, 0.68401599]),'score_time': array([ 0.24055672, 0.25055385, 0.24642444, 0.24583435, 0.25062966]),'test_f1_macro': array([ 0.93190598, 0.93358814, 0.92900074, 0.93620104, 0.93139325]),'test_precision_macro': array([ 0.93411186, 0.93509947, 0.93082131, 0.93790787, 0.93312355]),'test_recall_macro': array([ 0.93178571, 0.93357143, 0.92892857, 0.93607143, 0.93142857]),'train_f1_macro': array([ 0.95534592, 0.95516529, 0.95665886, 0.95573948, 0.95629695]),'train_precision_macro': array([ 0.95629235, 0.95618146, 0.95767379, 0.9566414 , 0.95725075]),'train_recall_macro': array([ 0.95526786, 0.95508929, 0.95660714, 0.95571429, 0.95625 ])}4.4 使用elastic net正則項的的線性回歸
def fit_and_predicted_with_ElasticNetCV(train_x, train_y, test_x, test_y):"""訓練與預測:param train_x: :param train_y: :param test_x: :param test_y: :return: """clf = linear_model.MultiTaskElasticNetCV().fit(train_x, train_y)predicted = clf.predict(test_x)print(metrics.classification_report(test_y, predicted))print('accuracy_score: %0.5fs' %(metrics.accuracy_score(test_y, predicted))) input_x, y = corpus input_x = feature_extractor(input_x, 'tfidf', max_df=i) # 3.切分訓練數據和測試數據 train_x, test_x, train_y, test_y = split_data_to_train_and_test([input_x, y]) # 4. 訓練以及測試 t0 = time() print('\t 使用線性回歸的文本分類\t\t\n' %(i)) fit_and_predicted_with_ElasticNetCV(train_x, train_y, test_x, test_y) print('time uesed: %0.4fs' %(time() - t0)) {'fit_time': array([ 0.69856882, 0.6891861 , 0.68457079, 0.68122745, 0.68401599]),'score_time': array([ 0.24055672, 0.25055385, 0.24642444, 0.24583435, 0.25062966]),'test_f1_macro': array([ 0.93190598, 0.93358814, 0.92900074, 0.93620104, 0.93139325]),'test_precision_macro': array([ 0.93411186, 0.93509947, 0.93082131, 0.93790787, 0.93312355]),'test_recall_macro': array([ 0.93178571, 0.93357143, 0.92892857, 0.93607143, 0.93142857]),'train_f1_macro': array([ 0.95534592, 0.95516529, 0.95665886, 0.95573948, 0.95629695]),'train_precision_macro': array([ 0.95629235, 0.95618146, 0.95767379, 0.9566414 , 0.95725075]),'train_recall_macro': array([ 0.95526786, 0.95508929, 0.95660714, 0.95571429, 0.95625 ])}線性模型有很多的變種,其的簡單高效并且可解釋性強等特點在機器學習領域有很廣泛的應用,這里不作進一步展開,大家自己科普吧~~
5. 總結
本文記錄了使用sklearn,采用線性回歸進行文本分類任務,在特征選擇哪里,進行TF-IDF的參數驗證部分,找到相對較好的max_df=0.5左右;
在選取好了特征后,我們對數據集進行交叉驗證,發現交叉驗證的方式能提高模型的效果,推薦在后面劃分數據集的時候使用交叉驗證。
我以邏輯回歸為例,進行了線性回歸分類器的參數搜索部分,然后利用最佳的參數,訓練了最佳的邏輯回歸文本分類模型,模型性能的acc值能達到:91%以上
最后,我們利用其它具有代表性的線性分類器進行相關實驗,但是沒有進行調參工作,其中L1產生比較離散的數值,elasticNet結合了L1,L2的優缺點,其在集合上的圖像介于兩者之間,效果在論文中比L1,L2都要好。
總結
以上是生活随笔為你收集整理的逻辑回归实现文本分类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 看完这篇文章,保你学会C语言switch
- 下一篇: 如何设置路由器连上电信宽带