本文使用sklearn
的逻辑斯谛回归模型,进行二分类预测,并通过调整各种参数,对预测结果进行对比。
逻辑斯谛回归模型( Logistic Regression,LR)
向量中非零元素个数,由于它没有一个好的数学表示,难以应用。
表示非零元素的绝对值之和
表示元素的平方和再开方
是损失项(训练误差),
项是对参数
的规则化函数,去约束模型,使之尽量简单,
为系数,在sklearn的参数中
。
sklearn中的LogisticRegression模型一文对模型的参数进行了说明
sklearn 中文文档 https://sklearn.apachecn.org/docs/0.21.3/
y=-x^2+1.5
为分类线的数据集'''
遇到不熟悉的库、模块、类、函数,可以依次:
1)百度(google确实靠谱一些),如"matplotlib.pyplot",会有不错的博客供学习参考
2)"终端-->python-->import xx-->help(xx.yy)",一开始的时候这么做没啥用,但作为资深工程师是必备技能
3)试着修改一些参数,观察其输出的变化,在后面的程序中,会不断的演示这种办法
'''
# written by hitskyer
# modified by Michael Ming on 2020.2.12
import sys
import numpy as np
import matplotlib.pyplot as plt
# sklearn 中文文档 https://sklearn.apachecn.org/docs/0.21.3/
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn import metrics
def get_parabolic_curve_data_set(n):
# 设置随机数的种子,以保证每回运行程序的随机结果一致
np.random.seed(520) # 520 可以随便写 Seed must be between 0 and 2**32 - 1
# 随机生成200个样本,每个样本两维特征
# X = np.random.normal(0, 1, size=(n, 2)) # 正态分布,中心0,标准差1
# 更改X的分布(正态,均匀),看看结果有什么变化
X = np.random.uniform(-4, 4, size=(n, 2)) # 均匀分布,区间[-4,4)
# 分类面(线)是y=-x^2+1.5,开口向下的抛物线,口内为1类,口外为0类
y = np.array(X[:, 0] ** 2 + X[:, 1] < 1.5, dtype=int) # 满足关系的为1,否则为0
# 加入10%的噪声数据
for _ in range(n // 10): # //为整除
y[np.random.randint(n)] = 1
return X, y
def show_data_set(X, y):
plt.scatter(X[y == 0, 0], X[y == 0, 1], c='r')
# 散点图,分量1,为y==0的行的0列,分量2,y==0的行的1列,c表示颜色
plt.scatter(X[y == 1, 0], X[y == 1, 1], c='b')
plt.show()
def PolynomialLogisticRegression(degree=2, C=1.0, penalty='l2'):
# 对输入特征依次做 多项式转换、归一化转换、类别预测,可以尝试注释掉前2个操作,看看结果有什么不同
return Pipeline([
# Pipeline 可以把多个评估器链接成一个。例如特征选择、标准化和分类
# 以多项式的方式对原始特征做转换,degree次多项式
('poly', PolynomialFeatures(degree=degree)),
# 对多项式转换后的特征向量做归一化处理,例如(数据-均值)/标准差
('std_scaler', StandardScaler()),
# 用转换后的特征向量做预测,penalty是正则化约束,C正则化强度,值越小,强度大
# solver 不同的求解器擅长的规模类型差异
# 正则化 https://blog.csdn.net/zouxy09/article/details/24971995/
('log_reg', LogisticRegression(C=C, penalty=penalty, solver="liblinear", max_iter=10000))
])
def plot_decision_boundary(x_min, x_max, y_min, y_max, pred_func):
h = 0.01
# 产生网格
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
# ravel将矩阵展平,np_c[a,b]将a,b按列拼在一起
Z = pred_func(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral) # 填充等高线
# 等高线参考 https://blog.csdn.net/lens___/article/details/83960810
def test(X_train, X_test, y_train, y_test, degree=2, C=1.0, penalty='l2'):
poly_log_reg = PolynomialLogisticRegression(degree=degree, C=C, penalty=penalty)
# 训练模型
poly_log_reg.fit(X_train, y_train)
# 在训练数据上做测试
predict_train = poly_log_reg.predict(X_train)
sys.stdout.write("LR(degree = %d, C=%.2f, penalty=%s) Train Accuracy : %.4g\n" % (
degree, C, penalty, metrics.accuracy_score(y_train, predict_train)))
# 在测试数据上做测试
predict_test = poly_log_reg.predict(X_test)
score = metrics.accuracy_score(y_test, predict_test)
sys.stdout.write("LR(degree = %d, C=%.2f, penalty=%s) Test Accuracy : %.4g\n" % (
degree, C, penalty, score))
print("--------------------------------------")
# 展示分类边界
plot_decision_boundary(-4, 4, -4, 4, lambda x: poly_log_reg.predict(x))
plt.scatter(X_train[y_train == 0, 0], X_train[y_train == 0, 1], color='r')
plt.scatter(X_train[y_train == 1, 0], X_train[y_train == 1, 1], color='b')
plt.xlabel("x1")
plt.ylabel("x2")
plt.rcParams['font.sans-serif'] = 'SimHei' # 消除中文乱码
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
plt.title("参数:degree:%d, C:%.2f, penalty:%s -- 准确率: %.4f" % (degree, C, penalty, score))
plt.show()
if __name__ == '__main__':
# 随机生成200个拥有2维实数特征 且 分类面(线)为y=-x^2+1.5(换言之,x2=-x1^2+1.5)的语料
X, y = get_parabolic_curve_data_set(200) # 可以加大数据量查看对结果的影响
# 预留30%作为测试语料
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
# 展示所生成的数据
show_data_set(X, y)
# 测试不同的超参数组合
print("准确率高,比较恰当的模型")
test(X_train, X_test, y_train, y_test, degree=2, C=1.0, penalty='l2')
print("准确率高,且恰当的模型")
test(X_train, X_test, y_train, y_test, degree=2, C=0.1, penalty='l2')
print("准确率高,但是过拟合的模型")
test(X_train, X_test, y_train, y_test, degree=20, C=1.0, penalty='l2')
print("准确率低,且过拟合的模型")
test(X_train, X_test, y_train, y_test, degree=20, C=0.1, penalty='l2')
print("准确率高,且恰当的模型")
test(X_train, X_test, y_train, y_test, degree=20, C=0.1, penalty='l1')
当样本为正态分布时:X = np.random.normal(0, 1, size=(n, 2)) # 正态分布,中心0,标准差1
参数组合 | 数据(正态分布) | deg = 2, C=1.00, l2 | deg = 2, C=0.10, l2 | deg = 20, C=1.00, l2 | deg = 20, C=0.10, l2 | deg = 20, C=0.10, l1 |
---|---|---|---|---|---|---|
200组数据、seed(520) | ||||||
准确率 | 0.9500 | 0.9500 | 0.9167 | 0.9333 | 0.9333 | |
200组数据、seed(777) | ||||||
准确率 | 0.9333 | 0.9333 | 0.9333 | 0.8833 | 0.9167 | |
2000组数据 seed(520) | ||||||
准确率 | 0.9333 | 0.9217 | 0.9317 | 0.9233 | 0.9317 | |
2000组数据 seed(777) | ||||||
准确率 | 0.9117 | 0.8983 | 0.9233 | 0.9067 | 0.9150 |
对比可以发现:
当样本为均匀分布时:X = np.random.uniform(-4, 4, size=(n, 2)) # 均匀分布,区间[-4,4)
参数组合 | 数据(均匀分布) | deg = 2, C=1.00, l2 | deg = 2, C=0.10, l2 | deg = 20, C=1.00, l2 | deg = 20, C=0.10, l2 | deg = 20, C=0.10, l1 |
---|---|---|---|---|---|---|
200组数据、seed(520) | ||||||
准确率 | 0.8500 | 0.8333 | 0.8333 | 0.8000 | 0.8333 | |
200组数据、seed(777) | ||||||
准确率 | 0.7833 | 0.7333 | 0.7667 | 0.7000 | 0.6667 | |
2000组数据 seed(520) | ||||||
准确率 | 0.8767 | 0.8750 | 0.9050 | 0.8900 | 0.8767 | |
2000组数据 seed(777) | ||||||
准确率 | 0.8883 | 0.8883 | 0.9050 | 0.9017 | 0.8950 |
Transformer
)(如多项式转换、归一化等等),最后一步必须是估计器(Estimator
)或叫分类器,输入数据经过转换器处理,输出的结果作为下一步的输入pipeline.fit
生成预测模型pipeline.predict
对数据进行预测上面实践代码里Pipeline
定义了模型将要做的事情:
PolynomialFeatures
(维度变化)StandardScaler
(让异常数据不要对正常数据造成很大影响)LogisticRegression
(预测)def PolynomialLogisticRegression(degree=2, C=1.0, penalty='l2'):
# 对输入特征依次做多项式转换、归一化转换、类别预测
return Pipeline([
# Pipeline 可以把多个评估器链接成一个。例如特征选择、标准化和分类
# 以多项式的方式对原始特征做转换,degree次多项式
('poly', PolynomialFeatures(degree=degree)),
# 对多项式转换后的特征向量做归一化处理,例如(数据-均值)/标准差
('std_scaler', StandardScaler()),
# 用转换后的特征向量做预测,penalty是正则化约束,C正则化强度,值越小,强度大
# solver 不同的求解器擅长的规模类型差异
# 正则化 https://blog.csdn.net/zouxy09/article/details/24971995/
('log_reg', LogisticRegression(C=C, penalty=penalty, solver="liblinear"))
])
在均匀分布的情况下,删除a、删除b、删除a,b,查看对预测的影响
参数组合, no ploy | 数据(均匀分布) | deg = 2, C=1.00, l2 | deg = 2, C=0.10, l2 | deg = 20, C=1.00, l2 | deg = 20, C=0.10, l2 | deg = 20, C=0.10, l1 |
---|---|---|---|---|---|---|
200组数据、seed(520) | ||||||
准确率 | 0.7333 | 0.7333 | 0.7333 | 0.7333 | 0.7333 | |
200组数据、seed(777) | ||||||
准确率 | 0.6333 | 0.6167 | 0.6333 | 0.6167 | 0.5667 | |
2000组数据 seed(520) | ||||||
准确率 | 0.6933 | 0.6933 | 0.6933 | 0.6933 | 0.6917 | |
2000组数据 seed(777) | ||||||
准确率 | 0.6867 | 0.6867 | 0.6867 | 0.6867 | 0.6867 |
ploy
多项式转换项后,模型维度没有增加,保持线性参数组合, no std_scaler | 数据(均匀分布) | deg = 2, C=1.00, l2 | deg = 2, C=0.10, l2 | deg = 20, C=1.00, l2 | deg = 20, C=0.10, l2 | deg = 20, C=0.10, l1 |
---|---|---|---|---|---|---|
200组数据、seed(520) | ||||||
准确率 | 0.8667 | 0.8667 | 0.8500 | 0.8500 | 0.8500(Warning) | |
200组数据、seed(777) | ||||||
准确率 | 0.8000 | 0.8000 | 0.7500 | 0.7500 | 0.7176(Warning) | |
2000组数据 seed(520) | 没跑出来 | |||||
准确率 | 0.8767 | 0.8783 | 0.7767 | 0.7767 | - | |
2000组数据 seed(777) | 没跑出来 | |||||
准确率 | 0.8883 | 0.8917 | 0.8100 | 0.8133 | - |
//报的Warning
ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
"the number of iterations.", ConvergenceWarning)
参数组合, no ploy & no scaler | 数据(均匀分布) | deg = 2, C=1.00, l2 | deg = 2, C=0.10, l2 | deg = 20, C=1.00, l2 | deg = 20, C=0.10, l2 | deg = 20, C=0.10, l1 |
---|---|---|---|---|---|---|
200组数据、seed(520) | ||||||
准确率 | 0.7333 | 0.7667 | 0.7333 | 0.7667 | 0.7500 | |
200组数据、seed(777) | ||||||
准确率 | 0.6167 | 0.6667 | 0.6167 | 0.6667 | 0.6333 | |
2000组数据 seed(520) | ||||||
准确率 | 0.6933 | 0.6933 | 0.6933 | 0.6933 | 0.6917 | |
2000组数据 seed(777) | ||||||
准确率 | 0.6850 | 0.6900 | 0.6850 | 0.6900 | 0.6883 |
import numpy as np
import matplotlib.pyplot as plt
# 计算x,y坐标对应的高度值
def f(x, y):
return x**2+y<1.5
# 生成x,y的数据
n = 256
x = np.linspace(-4, 4, n)
y = np.linspace(-4, 4, n)
# 把x,y数据生成mesh网格状的数据,在网格的基础上添加上高度值
X, Y = np.meshgrid(x, y)
# 填充等高线
plt.contourf(X, Y, f(X, Y))
# 显示图表
plt.show()
画出 x**2+y = 1.5
的等高线如下
np.r_
np.c_
用法参考下面:
>>> a
array([[1, 2, 3],
[7, 8, 9]])
>>> b
array([[4, 5, 6],
[1, 2, 3]])
>>> c=np.c_[a,b]
>>> c
array([[1, 2, 3, 4, 5, 6],
[7, 8, 9, 1, 2, 3]])
>>> c=np.r_[a,b]
>>> c
array([[1, 2, 3],
[7, 8, 9],
[4, 5, 6],
[1, 2, 3]])
>>> c=np.c_[a.ravel(),b.ravel()] # ravel()展平
>>> c
array([[1, 4],
[2, 5],
[3, 6],
[7, 1],
[8, 2],
[9, 3]])