相比《机器学习算法–python实现应用机器学习于情感分析-训练文档分类》将采用一种被称为“核外学习”的技术, 这种技术可以通过对数据集的小批增量来模拟分类器完成大型数据集的处理工作。
import pandas as pd
import numpy as np
import re
import pyprind # 评估机器学习算法的进度
import nltk
from nltk.corpus import stopwords
from collections import Iterator
from sklearn.feature_extraction.text import HashingVectorizer
from sklearn.linear_model import SGDClassifier
from distutils.version import LooseVersion as Version
from sklearn import __version__ as sklearn_version
stop = stopwords.words('english')
# 标记化函数
# tokenizer函数来清理来自于movie_data.csv文件的文本数据,
# 然后分解成单词,在标记的同时去除了停用词
def tokenizer(text):
text = re.sub('<[^>]*>', '', text)
emoticons = re.findall('(?::|;|=)(?:-)?(?:\)|\(|D|P)', text.lower())
text = re.sub('[\W]+', ' ', text.lower()) + ' '.join(emoticons).replace('-', '')
tokenized = [w for w in text.split() if w not in stop]
return tokenized
# 生成器函数stream_docs,每次读入并返回一个文档
def stream_docs(path):
with open(path, 'r', encoding='utf-8') as csv:
next(csv) # skip header
for line in csv:
text, label = line[:-3], int(line[-2])
yield text, label
# 该函数调用stream_docs读入文件流并返 回大小由参数size定义的文件
def get_minibatch(doc_stream, size):
docs, y = [], []
try:
for _ in range(size):
text, label = next(doc_stream)
docs.append(text)
y.append(label)
except StopIteration:
return None, None
return docs, y
'''
HashingVectorizer:
此策略具有以下优点:
它的内存非常低,可扩展到大型数据集,因为无需在内存中存储词汇表
它可以快速进行腌制和解开,因为除构造函数参数外,它不保存任何状态
它可以用于流(部分拟合)或并行管道中,因为在拟合过程中没有计算状态。
还有一些缺点(与使用带有内存词汇的CountVectorizer相比):
无法计算逆变换(从特征索引到字符串特征名称),这在尝试
反省哪些特征对模型最重要时可能会出现问题。
可能会发生冲突:不同的标记可以映射到相同的特征索引。但是在实践中,如果n_features足够大(例如,对于文本分类问题为2 ** 18),这几乎不是问题。
没有IDF加权,因为这会使变压器处于有状态。
'''
# 因为需要把全部的单词保存在内存,所以无法调用 CountVectorizer函数做核心学习。
# 另外,TfidfVectorizer需要把训练集的所有特征向量保存在内存,以计算逆文档频率。
# decode_error:遇到不能解码的字符将报UnicodeDecodeError错误,设为ignore将会忽略解码错误
# 分为三个处理步骤:preprocessing、tokenizing、n-grams generation.
vect = HashingVectorizer(decode_error='ignore',
n_features=2**21, # 如果在HashingVectorizer中选择较大的特征数, 可以减少哈希碰撞的机会,但是这么做也增加了逻辑回归模型系数的个数
preprocessor=None,
tokenizer=tokenizer)
print(Version(sklearn_version))
'''
可以选择损失函数:
loss=”hinge”: (soft-margin)线性SVM.
loss=”modified_huber”: 带平滑的hinge loss.
loss=”log”: logistic回归
通过penalty参数,可以设置对应的惩罚项。SGD支持下面的罚项:
penalty=”l2”: 对coef_的L2范数罚项
penalty=”l1”: 对coef_的L1范数罚项
penalty=”elasticnet”: L2和L1的convex组合; (1 - l1_ratio) * L2 + l1_ratio * L1
n_iter:迭代次数
'''
if Version(sklearn_version) < '0.18':
clf = SGDClassifier(loss='log', random_state=1, n_iter=1)
else:
clf = SGDClassifier(loss='log', random_state=1, max_iter=1)
doc_stream = stream_docs(path='xxx\\movie_data.csv')
pbar = pyprind.ProgBar(45) # 初始化进度条对象,迭代次数为45
classes = np.array([0, 1])
for _ in range(45): # 迭代45个迷你批次的文档
X_train, y_train = get_minibatch(doc_stream, size=1000)
if not X_train:
break
X_train = vect.transform(X_train)
clf.partial_fit(X_train, y_train, classes=classes) # 增量学习
pbar.update()
X_test, y_test = get_minibatch(doc_stream, size=5000)
X_test = vect.transform(X_test)
print('Accuracy: %.3f' % clf.score(X_test, y_test))
# 用前面的5000个文档来更新模型
clf = clf.partial_fit(X_test, y_test)
运行结果:
0.23.1
0% [##############################] 100% | ETA: 00:00:00
Total time elapsed: 00:00:21
Accuracy: 0.868