Sklearn增量训练报错:Pipeline无partial_fit属性及数据类型错误
我来帮你拆解一下遇到的两个问题,都是增量训练里的常见情况:
第一个坑:Pipeline不支持partial_fit
你用Pipeline的时候报错AttributeError: 'Pipeline' object has no attribute 'partial_fit',原因很直接:sklearn的默认Pipeline并没有实现增量训练的方法,除非Pipeline里的每一个组件(从向量器到分类器)都支持partial_fit才行。而你用的DictVectorizer在旧版sklearn里是不支持partial_fit的(新版0.21+虽然支持,但Pipeline还是不会自动帮你串联起来)。
解决办法很简单,手动拆分预处理和分类的步骤,自己控制每一批的特征转换:
from sklearn.feature_extraction import DictVectorizer from sklearn.linear_model import SGDClassifier import numpy as np # 初始化向量器和分类器 vec = DictVectorizer() clf = SGDClassifier(alpha=.0001, loss='log', penalty='l2', n_jobs=-1, verbose=1) # 处理第一批数据:向量器需要先学习特征空间,所以用fit_transform X_batch1, y_batch1 = X[:length], y[:length] X_vec_batch1 = vec.fit_transform(X_batch1) clf.partial_fit(X_vec_batch1, y_batch1, classes=np.array([0,1])) # 处理第二批数据:直接用之前学习到的特征空间做transform X_batch2, y_batch2 = X[length:], y[length:] X_vec_batch2 = vec.transform(X_batch2) # 这里不用再传classes参数了,只在第一次partial_fit时传 clf.partial_fit(X_vec_batch2, y_batch2)
这样就绕开了Pipeline的限制,手动完成了增量训练的流程。
第二个坑:SGDClassifier报TypeError: float() argument must be a string or a number
这个错误的核心是你直接把未做特征转换的原始数据传给了SGDClassifier。看你尝试的代码,你跳过了DictVectorizer的步骤,直接把transform_to_dataset输出的X(应该是字典列表)传给了SGDClassifier——而SGDClassifier只认数值型的特征矩阵,字典是无法直接转换成浮点数的,所以才会报错。
解决这个问题的关键就是:必须先对每一批数据做特征向量化,再传给分类器,也就是我上面给出的代码里的流程。另外你还要检查一下transform_to_dataset的输出,确保每个样本是键值对组成的字典,值都是字符串或数字(比如把词性标注、依存关系转成{'pos_NN': 1, 'dep_root': 1}这样的形式),不能有嵌套结构或者其他复杂类型。
额外的大数据量建议
如果你的数据量真的大到单机内存扛不住,可以试试Dask-ML的增量组件,它能帮你自动处理分批次的数据加载和训练,比如:
from dask_ml.wrappers import Incremental from dask_ml.feature_extraction import DictVectorizer as DaskDictVectorizer # 用Dask的向量器和增量包装器 vec = DaskDictVectorizer() clf = Incremental(SGDClassifier(alpha=.0001, loss='log', penalty='l2')) # 假设X是Dask的Bag或Array类型(可以用Dask加载大规模数据集) X_vec = vec.fit_transform(X) clf.fit(X_vec, y, classes=np.array([0,1]))
这样可以避免把所有数据加载到内存里,更适合超大规模的数据集。
内容的提问来源于stack exchange,提问作者kntgu

