我想使用名字来预测性别。不仅是名字,还包括从名字中提取的特征,如将“姓氏”作为从名字派生的特征。我的代码流程如下:将数据导入df > 指定lr分类器和dv字典向量化器 > 使用函数创建特征 > 执行字典向量化 > 训练。我想做以下几件事,但找不到任何相关资源。
1) 我想将预测的类别(0和1)添加回原始数据集或我可以看到名字和预测性别类别的数据集。目前我的y_test_predictions仅对应于X_test,这是一个稀疏矩阵。
2) 如何保留训练好的分类器,并用它来预测另一个包含许多名字的数据集的性别?以及如何仅插入一个名字“Rick Grime”,让分类器告诉我它预测的性别是什么?
我曾用nltk做过类似的事情,但在Scikit-learn中找不到任何示例或参考资料。
代码如下:
import pandas as pd from pandas import DataFrame, Series import numpy as np import re import random import time from random import randint import csv import sys from sklearn.metrics import classification_report from sklearn.linear_model import LogisticRegression from sklearn.svm import LinearSVC from sklearn.tree import DecisionTreeClassifier from sklearn.naive_bayes import MultinomialNB from sklearn.feature_extraction import DictVectorizer from sklearn.metrics import confusion_matrix as sk_confusion_matrix from sklearn.metrics import roc_curve, auc import matplotlib.pyplot as plt from sklearn.metrics import precision_recall_curve from sklearn import cross_validation data = pd.read_csv("file.csv", header=0, encoding="utf-8") df = DataFrame(data) dv = DictVectorizer() lr = LogisticRegression() X = df.raw_name.values X2 = df.name.values y = df.gender.values def feature_full_name(nameString): try: full_name = nameString if len(full_name) > 1: # 不接受只有一个字符的名字 return full_name else: return '?' except: return '?' def feature_full_last_name(nameString): try: last_name = nameString.rsplit(None, 1)[-1] if len(last_name) > 1: # 不接受只有一个字符的名字 return last_name else: return '?' except: return '?' def feature_name_entity(nameString2): space = 0 try: for i in nameString2: if i == ' ': space += 1 return space+1 except: return 0 my_dict = [{'last-name': feature_full_last_name(i)} for i in X] my_dict2 = [{'name-entity': feature_name_entity(feature_full_name(i))} for i in X2] all_dict = [] for i in range(0, len(my_dict)): temp_dict = dict( my_dict[i].items() + my_dict2[i].items() ) all_dict.append(temp_dict) newX = dv.fit_transform(all_dict) X_train, X_test, y_train, y_test = cross_validation.train_test_split(newX, y, test_size=0.3) lr.fit(X_train, y_train) y_test_predictions = lr.predict(X_test)
回答:
我会使用Scikit-learn的一些内置工具来分割数据框,向量化名字,并预测结果。然后你可以将预测结果添加回测试数据框中。例如,使用一小组名字作为示例:
data = {'Bruce Lee': 'Male', 'Bruce Banner': 'Male', 'Bruce Springsteen': 'Male', 'Bruce Willis': 'Male', 'Sarah McLaughlin': 'Female', 'Sarah Silverman': 'Female', 'Sarah Palin': 'Female', 'Sarah Hyland': 'Female'}import pandas as pddf = pd.DataFrame.from_dict(data, orient='index').reset_index()df.columns = ['name', 'gender']print(df) name gender0 Sarah Silverman Female1 Sarah Palin Female2 Bruce Springsteen Male3 Bruce Banner Male4 Bruce Lee Male5 Sarah Hyland Female6 Sarah McLaughlin Female7 Bruce Willis Male
现在我们可以使用Scikit-learn的CountVectorizer
来统计名字中的单词;这基本上与你上面所做的输出相同,只是它不根据名字长度等进行过滤。为了便于使用,我们将它与交叉验证的逻辑回归放在一个管道中:
from sklearn.feature_extraction.text import CountVectorizerfrom sklearn.linear_model import LogisticRegressionCVfrom sklearn.pipeline import make_pipelineclf = make_pipeline(CountVectorizer(), LogisticRegressionCV(cv=2))
现在我们可以将数据分割成训练/测试集,拟合管道,然后分配结果:
from sklearn.cross_validation import train_test_splitdf_train, df_test = train_test_split(df, train_size=0.5, random_state=0)clf.fit(df_train['name'], df_train['gender'])df_test = df_test.copy() # 这样我们可以修改它df_test['predicted'] = clf.predict(df_test['name'])print(df_test) name gender predicted6 Sarah McLaughlin Female Female2 Bruce Springsteen Male Male1 Sarah Palin Female Female7 Bruce Willis Male Male
同样,我们可以将一个名字列表传递给管道并获得预测:
>>> clf.predict(['Bruce Campbell', 'Sarah Roemer'])array(['Male', 'Female'], dtype=object)
如果你想在文本向量化中使用更复杂的逻辑,你可以为你的输入数据创建一个自定义转换器:在网上搜索“scikit-learn custom transformer”应该能给你提供一组不错的示例来参考。
编辑:这里有一个使用自定义转换器从输入名字生成字典的示例:
from sklearn.base import TransformerMixinclass ExtractNames(TransformerMixin): def transform(self, X, *args): return [{'first': name.split()[0], 'last': name.split()[-1]} for name in X] def fit(self, *args): return selftrans = ExtractNames()>>> trans.fit_transform(df['name'])[{'first': 'Bruce', 'last': 'Springsteen'}, {'first': 'Bruce', 'last': 'Banner'}, {'first': 'Sarah', 'last': 'Hyland'}, {'first': 'Sarah', 'last': 'Silverman'}, {'first': 'Sarah', 'last': 'Palin'}, {'first': 'Bruce', 'last': 'Lee'}, {'first': 'Bruce', 'last': 'Willis'}, {'first': 'Sarah', 'last': 'McLaughlin'}]
现在你可以将它与DictVectorizer
放在一个管道中来生成稀疏特征:
from sklearn.feature_extraction import DictVectorizerfrom sklearn.pipeline import make_pipelinepipe = make_pipeline(ExtractNames(), DictVectorizer())>>> pipe.fit_transform(df['name'])<8x10 sparse matrix of type '<class 'numpy.float64'>' with 16 stored elements in Compressed Sparse Row format>
最后,你可以创建一个结合这些与交叉验证的逻辑回归的管道,并按上面的方法进行操作:
clf = make_pipeline(ExtractNames(), DictVectorizer(), LogisticRegressionCV())clf.fit(df_train['name'], df_train['gender'])df_test['predicted'] = clf.predict(df_test['name'])
从这里开始,如果你愿意,你可以修改ExtractNames
转换器来进行更复杂的特征提取(使用你上面的一些代码),你最终会得到一个管道实现你的过程,但让你可以简单地在输入字符串列表上调用predict()
。希望这对你有帮助!