我正在尝试编写一个使用Python(和Scikit Learn)来自动分类银行交易的代码。目前我已经有大约1.7k个已分类的交易,包含70个类别(标签) – 我总共有大约3.5k行数据,但并非所有都已整理,这是我第一次尝试。基本情况是我已经导入了一个包含以下内容的CSV文件:
Description | Value | LabelRSHOP-SABORES DA -25/04 | -30 | RestaurantsRSHOP-MERCATTINO -28/04 | -23 | BarsRSHOP-HORTISABOR -07/05 | -65 | SupermarketTBI 3712.06663-9 tokpag | 1.000 | Salary
描述和数值是我的特征,而标签,就是我的标签。描述可能会因为不同的字符等变得有些复杂。所以我了解到我应该使用Tf-IDF来向量化描述,并对标签使用LabelEncode进行编码。
目前,我有以下代码:
# Loads datadata = pd.read_csv('classifications.csv', encoding='latin1', error_bad_lines=False, delimiter=';')# Assigns features and labels - I chose to use only the description to make it simpler for a first time. I want to use the value later as well.data.columns = ['desc', 'value', 'label']data_base = data.valuesfeatures_base= data_base[:,[0]]labels_base = data_base[:,[2]]# Printing features returns a (1722,1) array - looks good.print(features_base.shape)# Printing labels returns a (1722,1) array - looks good.print(labels_base.shape)# Encodes labels, printing returns (1722,) - don't know why the "1" is missing on the y.encoder = LabelEncoder()label_encoded = encoder.fit_transform((labels_base.astype(str)).ravel())print(label_encoded.shape)# Encodes features. Printing returns (1722, 1012) - don't know what's the "1012" on the y axis... the only thing I can think of the number of unique values on the vector, but can't be sure.vectorizer = TfidfVectorizer()vectors = vectorizer.fit_transform(features_base.ravel().astype('U'))print(vectors.shape)#Testtrain_features, train_labels, test_features, test_labels = tts(vectors, label_encoded, test_size=0.2)
然后我尝试了几种不同的估计器,每个都有不同的错误(在第一行注释中写明):
# Random Forest Classifier - returns "ValueError: Unknown label type: 'continuous-multioutput'"clf1 = RandomForestClassifier()print("Using", clf1)clf1.fit(train_features.toarray(), train_labels.toarray())predictions1 = clf1.predict(test_features)print( "\nPredictions:", predictions1)score = 0for i in range(len(predictions1)): if predictions[i] == test_labels[i]: score += 1print("Accuracy:", (score / len(predictions)) * 100, "%")# Decision Tree Classifier - returns "ValueError: Unknown label type: 'continuous-multioutput'"clf2 = tree.DecisionTreeClassifier()print("Using", clf2)clf2.fit(train_features.toarray(), train_labels.toarray())predictions2 = clf2.predict(test_features)print( "\nPredictions:", predictions2)score = 0for i in range(len(predictions2)): if predictions[i] == test_labels[i]: score += 1print("Accuracy:", (score / len(predictions)) * 100, "%")#SVC Linear - returns "ValueError: bad input shape (345, 1012)"clf3 = svm.SVC(kernel='linear')print("Using", clf3)clf3.fit(train_features, train_labels)predictions3 = clf3.predict(test_features)print( "\nPredictions:", predictions3)score = 0for i in range(len(predictions1)): if predictions[i] == test_labels[i]: score += 1print("Accuracy:", (score / len(predictions)) * 100, "%")# SVC Non Linear - returns "ValueError: bad input shape (345, 1012)"clf4 = svm.SVC()print("Using", clf4)clf4.fit(train_features.toarray(), train_labels.toarray())predictions4 = clf4.predict(test_features)print( "\nPredictions:", predictions4)score = 0for i in range(len(predictions1)): if predictions[i] == test_labels[i]: score += 1print("Accuracy:", (score / len(predictions)) * 100, "%")
最终目标是加载一个包含描述/金额的CSV文件,并建议一个标签(最好能知道建议的确定性水平)。
总结如下:
- 向量化描述文本的方法合理吗?有什么建议吗?
- 使用LabelEncoder来向量化我的标签是正确的吗?
- 我哪里做错了?代码中的错误是什么?
非常感谢。
回答:
问题出在标签上,因为你使用了pandas,它们应该以分类数据的形式传递给分类器。我几分钟后会发布一些代码。
更新:所以你的代码有一些问题。在开发新的机器学习模型时,我建议从简单开始,然后在你有一个工作原型后再增加其复杂性。我只为RandomForestClassifier实现了代码,你应该能够轻松地为你感兴趣的其他分类器复制它。以下是代码:
import pandas as pdfrom sklearn.feature_extraction.text import TfidfVectorizerfrom sklearn.ensemble import RandomForestClassifierdata = pd.read_csv('classifications.csv', encoding='latin1', error_bad_lines=False, delimiter=';')data.columns = ['desc', 'value', 'label']data['label'] = data['label'].astype('category')data.info()vectorizer = TfidfVectorizer()vectors = vectorizer.fit_transform(data['desc'])print('Shape: ',vectors.shape)clf = RandomForestClassifier(random_state=42)clf.fit(vectors,data['label'])print('Score: {}'.format(clf.score(vectors,data['label'])))clf.predict(vectorizer.transform(data['desc']))
这段代码的输出是:
<class 'pandas.core.frame.DataFrame'>RangeIndex: 4 entries, 0 to 3Data columns (total 3 columns):desc 4 non-null objectvalue 4 non-null float64label 4 non-null categorydtypes: category(1), float64(1), object(1)memory usage: 340.0+ bytesShape: (4, 14)Score: 1.0array(['Restaurants', 'Bars', 'Supermarket', 'Salary'], dtype=object)
几点评论:
1) 如果你使用pandas,分类标签最好是分类数据(pandas.Categorical)。这减少了分类器将标签解释为有序数据并尝试相应地排序其预测的可能性。
2) 如果你正在链接多个来自sklearn的对象,比如一个向量化器和一个分类器,最好通过编写来实例化一个Pipeline对象
from sklearn.pipeline import Pipelinepipeline = Pipeline([('vectorizer',TfidfVectorizer()), ('classifier',RandomForestClassifier())])
这可以节省你每次需要给分类器提供新数据时,都必须将向量化器的.transform或.fit_transform方法的输出传递给分类器的麻烦,因为Pipeline会自动完成这些操作。
3) 为随机分类器设置一个random_state,以确保结果的可重复性。
4) 不清楚你为什么要手动计算分数:分类器的.score()方法会自动计算平均准确率分数,并且可以防止你因像len(predictions)这样的函数而犯错。当你将来尝试预测概率分布而不是单个点预测时,如果你养成了调用len(predictions)的习惯,你可能会在数组的错误维度上进行计算,而不易察觉。然而,如果你想得到百分比形式的分数,而不是0到1的范围内,只需将.score()方法返回的分数乘以100即可。
希望这对你有帮助。