这里的数据集我们使用内置的鸢尾花数据集来进行测试,
from sklearn.datasets import load_iris
iris = load_iris()
将鸢尾花数据集的数据和标签分割成训练集和测试集,其中测试集占比20%.
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(iris['data'], iris['target'], test_size=0.2)
在这里使用逻辑回归进行拟合数据
from sklearn.linear_model import LogisticRegression
# 以下参数依次表示,使用拟牛顿法求局部最优,多分类的训练方式是 'ovr',最大迭代次数是5000,随机状态为42
clf = LogisticRegression(solver='lbfgs', multi_class='ovr', max_iter=5000, random_state=42)
clf.fit(X_train, y_train)
accuracy = clf.score(X_test, y_test)
print('Accuracy score of the {} is {:.2f}'.format(clf.__class__.__name__, accuracy))
怎么样,使用起来是不是很简单,使用不同的模型需要改变的就只有上面的 LogisticRegression
分类器和里面的参数,这些改变起来非常的容易,下面我们再对这个流程进行优化。
在对数据进行拟合前,我们可以对数据进行一些预处理操作,比如归一化。归一化将数据都化为[0,1]区间内,这样能够十分有效的节省迭代的次数,这是很简单的一个道理,拟合[0,1] 区间的数据肯定会比拟合不知多大界限的数据要容易得多,sklearn中的标准化一般使用 MinMaxScaler
类,使用缩放器对其进行缩放,示例如下:
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
注意,我们不能对测试集中的数据也使用 fit_transform
函数, 该函数实际上是将 fit
函数与 transform
函数合在一起了, fit
函数能够找到数据集中的最大值与最小值, transform
函数能够使用该最大最小值对数据进行归一化缩放,测试集中的数据是不允许进行改变的,如果对测试集中的数据也使用 fit_transform
则相当于改变了测试集数据的标准,所以仅对测试集使用 transform
函数来统一标准。
上述过程其实可以使用管道进行简化,可以在管道中对想要进行的估计器进行命名,pipe 简化后的代码如下所示:
from sklearn.pipeline import make_pipeline
pipe = make_pipeline(MinMaxScaler(),
LogisticRegression(solver='lbfgs', multi_class='auto', random_state=42, max_iter=1000))
pipe.fit(X_train, y_train)
accuracy = pipe.score(X_test, y_test)
print('Accuracy score of the {} is {:.2f}'.format(pipe.__class__.__name__, accuracy))
当数据较少时,由于我们还要对其进行训练集和测试集的分割,所以可使用的数据更加少了,这种情况下一般会使用K折交叉验证法来对数据进行验证,这种情况下也就不需要对数据进行训练集与测试集的分割了,具体方法陈述如下。
K K K 折交叉验证:首先随机地将已给数据切分为 K K K 个互不相交、大小相同的子集;然后利用 K − 1 K-1 K−1 个子集的数据训练模型,利用余下的子集测试模型;将这一过程对可能的 K K K 种选择重复进行;最后选出 K K K 次评测中平均测试误差最小的模型。
使用交叉验证的代码如下:
from sklearn.model_selection import cross_val_score
pipe = make_pipeline(MinMaxScaler(),
LogisticRegression(solver='lbfgs', multi_class='auto', random_state=42, max_iter=1000))
# 参数依次为传入的模型,数据集,标签集,cv为K折交叉中K的值,scoring为评价函数
score = cross_val_score(pipe, iris['data'],iris['target'],cv=10,scoring='accuracy')
print(score.mean())
返回的 score
是一个列表,列表中的每一项是每一折拟合的评分,最后对其进行求平均值进行评估。
管道组件的参数不同,其准确度也是不一样的,很多时候我们都想找到最适合的参数来对模型进行拟合,从而获得最佳精度。
我们可以使用 pipe.get_params()
来获得管道的参数,如以上模型使用该函数输出管道的参数如下:
{'memory': None,
'steps': [('minmaxscaler', MinMaxScaler()), ('logisticregression', LogisticRegression(max_iter=1000, random_state=42))],
'verbose': False,
'minmaxscaler': MinMaxScaler(),
'logisticregression': LogisticRegression(max_iter=1000, random_state=42),
'minmaxscaler__clip': False,
'minmaxscaler__copy': True,
'minmaxscaler__feature_range': (0, 1),
'logisticregression__C': 1.0,
'logisticregression__class_weight': None,
'logisticregression__dual': False,
'logisticregression__fit_intercept': True,
'logisticregression__intercept_scaling': 1,
'logisticregression__l1_ratio': None,
'logisticregression__max_iter': 1000,
'logisticregression__multi_class': 'auto',
'logisticregression__n_jobs': None,
'logisticregression__penalty': 'l2',
'logisticregression__random_state': 42,
'logisticregression__solver': 'lbfgs',
'logisticregression__tol': 0.0001,
'logisticregression__verbose': 0,
'logisticregression__warm_start': False}
一个简单的调节超参数的想法是循环进行拟合,选择拟合精度最高的那一组参数作为最终的拟合模型,sklearn的 GridSearchCV
函数能够帮我们做到。
比如我们希望优化上述管道的分类 logisticregression__C
和 logisticregression__penalty
参数,则有下述代码,
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import MinMaxScaler
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import GridSearchCV
iris = load_iris()
pipe = make_pipeline(MinMaxScaler(),
LogisticRegression(solver='saga', multi_class='auto', random_state=42, max_iter=10000))
param_grid = {'logisticregression__C': [0.1, 1.0, 10],
'logisticregression__penalty': ['l2', 'l1']}
# 参数依次是估计器,优化的参数,K折检验,n_jobs=-1表示使用全部的cpu
grid = GridSearchCV(pipe, param_grid=param_grid, cv=3, n_jobs=-1, return_train_score=True, scoring='accuracy')
grid.fit(iris['data'], iris['target'])
# 得到要搜索的参数中最优的参数组合
print(grid.best_params_)
我们知道刚才都使用的是数值类型的数据来对模型进行拟合,但是,实际中我们不可避免地会使用数值类型的数据,二这些非数值类型的数据我们称之为异构数据。
在这里我们使用泰坦尼克号中的幸存者遇难者数据来进行示例,数据我放到网盘上(提取码1234),需要用到的自行下载。
这里使用pandas对训练集中的数据进行导入。
import pandas as pd
data = pd.read_csv(r'F:data\titanic\train.csv', na_values='?')
print(data.head())
我们需要预测的是其是否幸存或死亡,所以我们的 x
为除了幸存之外的所有列, y
为幸存那一列。
from sklearn.model_selection import train_test_split
y = data['Survived']
# 将无用的数据以及结果删掉
x = data.drop(columns=['Survived','PassengerId', 'Pclass', 'Name', 'Ticket'])
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=42)
一般情况下我们是像下面一样拟合的,但是这样的话由于存在异构数据,一定会报错。
from sklearn.linear_model import LogisticRegression
clf = LogisticRegression()
clf.fit(X_train, y_train)#这里肯定会报错。
为了不让这些异构数据影响到我们的训练,我们需要想办法将异构数据转为数值类型的数据,最常用最简单的办法就是使用 one-hot
矩阵。
我们以 sex
与 embarked
列,将使用 SimpleImputer
用常量值替换缺失值,对其做one-hot编码如下:
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder
from sklearn.pipeline import make_pipeline
ohe = make_pipeline(SimpleImputer(strategy='constant'), OneHotEncoder())
X_encoded = ohe.fit_transform(x_train[['Sex', 'Embarked']])
print(X_encoded.toarray())
输出时可以看到矩阵的值为数值,可以进行拟合。
在处理数据时,我们要将数值和能够转化为 one-hot 矩阵的列分开来,如下:
col_cat = ['Sex', 'Embarked']
col_num = ['Age', 'Sibsp', 'Parch', 'Fare']
pipe_cat = make_pipeline(SimpleImputer(strategy='constant'), OneHotEncoder(handle_unknown='ignore'))
pipe_num = make_pipeline(SimpleImputer(), StandardScaler())
# 能够将多个列管道拼接在一起,在拼接时未指定的列将会被删掉
preprocessor = make_column_transformer((pipe_cat, col_cat), (pipe_num, col_num))
pipe = make_pipeline(preprocessor, LogisticRegression(solver='lbfgs'))
pipe.fit(x_train, y_train)
accuracy = pipe.score(x_test, y_test)
print('Accuracy score of the {} is {:.2f}'.format(pipe.__class__.__name__, accuracy))
当然,这里也可以使用网格搜索 GridSearchCV
来对参数进行优化。
准确度
将二分类中的分类指标与预测的分类指标进行比对,计算出其中分类准确的样本比例,公式如下:
a c c = ∑ i = 1 N I ( y i ^ = y ) acc=\sum_{i=1}^NI(\hat{y_i}=y) acc=i=1∑NI(yi^=y)sklearn中的函数如下所示
from sklearn.metrics import accuracy_score
acc=accuracy_score(y_true, y_pred, normalize=True, sample_weight=None)
参数如下:
y_true
:数据的真实label值
y_pred
:数据的预测标签值
normalize
:默认为True,返回正确预测的个数,若是为False,返回正确预测的比例
sample_weight
:样本权重
混淆矩阵
from sklearn.metrics import confusion_matrix
conf = confusion_matrix(y_true,y_pred,labels=None,sample_weight = None)
y_true
:真实的label,一维数组,列名
y_pred
:预测值的label,一维数组,行名
labels
:默认不指定,此时y_true,y_pred去并集,做升序,作为label
sample_weight
:样本权重
返回结果:返回混淆矩阵
精确度和召回率
设 TP
:将正类预测为正类数;FN
:将正类预测为负类数;FP
:将负类预测为正类数;TN
:将负类预测为负类数。
则精确度 P
和 召回率 R
表示如下:
P = T P T P + F P P= \frac{TP}{TP+FP} P=TP+FPTP R = T P T P + F N R=\frac{TP}{TP+FN} R=TP+FNTP那么设置召回率有什么作用呢?假设我们现在有100个患者样本,其中有5个患者患有癌症,我们用1表示,其余95名正常患者我们用0表示。假设我们现在用一种算法做预测,所有的结果都预测为0,95个肯定是预测对了,算法准确率为95%,看着挺高的。但是这个算法对于癌症患者的预测准确率是0,所以这个算法是没有任何意义的。这时候我们的recall值的价值就体现出来了,recall值是在5个癌症患者中找能预测出来的,如果预测3个对了,recall = 60%。
在sklearn中,精确度与召回率函数如下:
from sklearn.metrics import classification_report
classification=classification_report(y_true, y_pred, labels=None,
target_names=None, sample_weight=None, digits=2, output_dict=False)
y_true
:真实的label,一维数组,列名
y_pred
:预测值的label,一维数组,行名
labels
:默认不指定,此时y_true,y_pred去并集,做升序,做label
sample_weight
:样本权重
target_names
:行标签,顺序和label的要一致
digits
:整型,小数的位数
out_dict
:输出格式,默认False,如果为True,输出字典。
输出为一个矩阵形式,包含每一个类别的精确度,召回率,F1。
多分类经常使用的评价指标也是上面三个,那就如上所示吧
决定系数R2
决定系数R2定义如下所示
R 2 = 1 − ∑ i = 0 N ( y i − y i ^ ) 2 ∑ i = 0 N ( y i − y i ˉ ) 2 R2=1-\frac{\sum_{i=0}^N(y_i-\hat{y_i})^2}{\sum_{i=0}^N(y_i-\bar{y_i})^2} R2=1−∑i=0N(yi−yiˉ)2∑i=0N(yi−yi^)2决定系数,也称为拟合优度。是相关系数的平方。表示可根据自变量的变异来解释因变量的变异部分。决定系数的大小决定了相关的密切程度。意义:拟合优度越大,自变量对因变量的解释程度越高,自变量引起的变动占总变动的百分比高。观察点在回归直线附近越密集。
在sklearn中的使用如下:
from sklearn.metrics import r2_score
r2 = r2_score(y_true,y_pred)
均方差
均方差是最常用的回归问题的损失函数,表示如下:
M S E = 1 N ∑ i = 1 N ( y i − y i ^ ) 2 MSE= \frac{1}{N}\sum_{i=1}^N(y_i-\hat{y_i})^2 MSE=N1i=1∑N(yi−yi^)2在sklearn中的使用如下所示:
from sklearn.metrics import mean_squared_error # 均方误差
mse = mean_squared_error(y_true,y_pred)