集合算法
正向激励
首先为样本矩阵中每行样本随机分配初始权重, 由此构建一棵带有权重的决策树. 在使用该决策树提供预测输出时, 通过加权平均或加权投票的方式产生预测值. 将训练样本代入模型, 预测输出, 对那些预测失败的样本, 提高其权重, 由此形成第二棵树. 重复以上过程, 构建出不同权重的n棵树.
正向激励相关API:
import sklearn.tree as st
import sklearn.ensemble as se
# 普通决策树模型
model = st.DecisionTreeRegressor(max_depth=4)
# 使用正向激励集合算法构建400棵树
se.AdaBoostRegressor(model, # 基础模型 n_estimators=400, random_state=7)
model.fit(train_x, train_y)
test_y = model.predict(test_x)
特征重要性
作为决策树模型训练过程的副产品, 根据每个特征划分子表前后的信息熵减少量就标志了该特征的重要程度, 此即为该特征的特征重要性指标. 通过模型训练得到的model对象提供了属性: feature_importances_来存储每个特征的特征重要性指标值.
获取特征重要性相关API:
model.fit(train_x, train_y)
fi = model.feature_importances_
案例:
boston = sd.load_boston()
print(boston.data.shape)
print(boston.target.shape)
print(boston.feature_names)
# 打乱原始输入输出数据集, 划分测试集, 训练集, random_state: 随机种子, 一致打乱结果一致
x, y = su.shuffle(boston.data, boston.target, random_state=6)
# 划分测试集, 训练集
train_size = int(len(x) * 0.8)
train_x, train_y, test_x, test_y = x[:train_size], y[:train_size], \
x[train_size:], y[train_size:]
# 创建模型, 使用训练集进行模型训练
model = st.DecisionTreeRegressor(max_depth=10)
model.fit(train_x, train_y)
# 测试集数据预测
pred_test_y = model.predict(test_x)
# 输出R2得分
print(sm.r2_score(test_y, pred_test_y))
# 单颗决策树模型, 输出特征重要性
fi = model.feature_importances_
# 把特征重要性绘制成柱状图
mp.figure('Feature Importance')
mp.subplot(211)
mp.title('Decision Tree', fontsize=14)
mp.ylabel('Importance', fontsize=12)
mp.grid(linestyle=':')
indices = fi.argsort()[::-1]
pos = np.arange(indices.size)
mp.bar(pos, fi[indices], label='DT', facecolor='deepskyblue', edgecolor='steelblue')
mp.xticks(pos, boston.feature_names[indices])
mp.legend()
model = se.AdaBoostRegressor(
model,
n_estimators=400,
random_state=7
)
model.fit(train_x, train_y)
pred_test_y = model.predict(test_x)
print(sm.r2_score(test_y, pred_test_y))
# 基于决策树的正向激励, 输出特征重要性
fi2 = model.feature_importances_
mp.subplot(212)
mp.title('AdaBoostRegressor', fontsize=14)
mp.ylabel('Importance', fontsize=12)
mp.grid(linestyle=':')
indices = fi2.argsort()[::-1]
pos = np.arange(indices.size)
mp.bar(pos, fi2[indices], label='AD', facecolor='deepskyblue', edgecolor='steelblue')
mp.xticks(pos, boston.feature_names[indices])
mp.legend()
mp.show()
自主集合
每次从总样本矩阵中以有放回抽样的方式随机抽取部分样本构建决策树, 这样形成多棵包含不同训练样本的决策树. 以削弱某些强势样本对模型预测结果的影响, 提高模型的普适性.
随机森林
在自主集合基础上, 每次构建决策树模型时, 不仅随机选择部分样本, 而且还随机选择部分特征, 这样的集合算法, 不仅规避了强势样本对预测结果的影响, 而且也削弱了强势特征的影响, 使模型的预测能力更加泛化.
随机森林相关API:
import sklearn.ensemble as se
# 随机森林回归器
se.RandomForestRegressor(
max_depth=10, # 树的最大深度
n_estimators=1000, # 决策树的数量
min_samples_split=2 # 子表中最小样本数, 小于该值不再拆分
)
案例: 分析共享单车的需求, 从而判断如何进行共享单车的投放.
data = np.loadtxt('bike_day.csv', unpack=False, dtype='U20', delimiter=',')
day_headers = data[0, 2:13]
x = np.array(data[1:, 2:13], dtype=float)
y = np.array(data[1:, -1], dtype=float)
x, y = su.shuffle(x, y, random_state=7)
train_size = int(len(x) * 0.9)
train_x, train_y, test_x, test_y = \
x[:train_size], y[:train_size],\
x[train_size:], y[train_size:]
# 随机森林回归器
model = se.RandomForestRegressor(
max_depth=10, n_estimators=1000,
min_samples_split=2
)
model.fit(train_x, train_y)
pred_test_y = model.predict(test_x)
print(sm.r2_score(test_y, pred_test_y))
fi = model.feature_importances_
indices = fi.argsort()[::-1]
print(day_headers[indices])
print(fi[indices])
data = np.loadtxt('bike_hour.csv', unpack=False, dtype='U20', delimiter=',')
hour_headers = data[0, 2:14]
x = np.array(data[1:, 2:14], dtype=float)
y = np.array(data[1:, -1], dtype=float)
x, y = su.shuffle(x, y, random_state=7)
train_size = int(len(x) * 0.9)
train_x, train_y, test_x, test_y = \
x[:train_size], y[:train_size],\
x[train_size:], y[train_size:]
# 随机森林回归器
model = se.RandomForestRegressor(
max_depth=10, n_estimators=1000,
min_samples_split=2
)
model.fit(train_x, train_y)
pred_test_y = model.predict(test_x)
print(sm.r2_score(test_y, pred_test_y))
fi = model.feature_importances_
indices = fi.argsort()[::-1]
print(hour_headers[indices])
print(fi[indices])
人工分类(简单分类)
特征1 | 特征2 | 输出 |
3 | 1 | 0 |
2 | 5 | 1 |
1 | 8 | 1 |
6 | 4 | 0 |
5 | 2 | 0 |
3 | 5 | 1 |
4 | 7 | 1 |
4 | -1 | 0 |
... | ... | ... |
6 | 8 | ? |
x = np.array([
[3, 1],
[2, 5],
[1, 8],
[6, 4],
[5, 2],
[3, 5],
[4, 7],
[4, -1],
])
y = np.array([0, 1, 1, 0, 0, 1, 1, 0])
# 绘制类别边界线
l, r = x[:, 0].min() - 1, x[:, 0].max() + 1
b, t = x[:, 1].min() - 1, x[:, 0].max() + 1
n = 500
grid_x, grid_y = np.meshgrid(
np.linspace(l, r, n),
np.linspace(b, t, n)
)
grid_z = np.piecewise(grid_x,
[grid_x < grid_y, grid_x >= grid_y],
[1, 0])
print(grid_z)
# 绘制matplotlib绘制这些点, 以及点的类别
mp.figure('Simple Classification', facecolor='lightgray')
mp.title('Simple Classification', fontsize=16)
mp.xlabel('Feature1', fontsize=12)
mp.ylabel('Feature2', fontsize=12)
mp.tick_params(labelsize=10)
# 为网格点坐标矩阵填充颜色
mp.pcolormesh(grid_x, grid_y, grid_z, cmap='gray')
mp.scatter(x[:, 0], x[:, 1], c=y, cmap='brg', s=80)
mp.show()
逻辑分类
通过输入的样本数据, 基于多元线性回归模型求出线性预测方程.
y = w0 + w1x1 + w1x1
x1 x2 y
7 13 0.823432124
12 4 0.234562232
......
但通过线性回归方程返回的是连续值, 不可以直接用于分类业务模型, 所以亟需一种方式使得把连续的预测值->离散的预测值.[-♾, +♾]->(0,1)
逻
辑
函
数
:
y
=
1
1
+
e
−
x
逻辑函数: y = \frac{1}{1+e^{-x}}
逻辑函数:y=1+e−x1
该逻辑函数当x>0, y>0.5;当x<0, y<0.5;可以把样本数据经过线性预测模型求得的值代入逻辑函数的x, 即将预测函数的输出看做被划分成为1类别的概率, 则概率大的类别作为预测结果, 就可以根据函数值确定两个分类. 这也是线性函数非线性化的一种方式.
逻辑回归相关API:
import sklearn.linear_model as lm
# 创建逻辑分类模型
# solve: liblinear指的时逻辑分类底层使用线性回归模型
m = lm.LogisticRegression(solver='liblinear', C=正则强度)
m.fit(训练输入, 训练输出)
m.predict(测试输入)
案例:
x = np.array([
[3, 1],
[2, 5],
[1, 8],
[6, 4],
[5, 2],
[3, 5],
[4, 7],
[4, -1],
])
y = np.array([0, 1, 1, 0, 0, 1, 1, 0])
# 绘制类别边界线
l, r = x[:, 0].min() - 1, x[:, 0].max() + 1
b, t = x[:, 1].min() - 1, x[:, 0].max() + 1
n = 500
grid_x, grid_y = np.meshgrid(
np.linspace(l, r, n),
np.linspace(b, t, n)
)
m = lm.LogisticRegression(solver='liblinear', C=1)
m.fit(x, y)
samples = np.column_stack( # 将两个数组组合成为一个两列的二维数组
(grid_x.ravel(), grid_y.ravel())
)
grid_z = m.predict(samples)
grid_z = grid_z.reshape(grid_x.shape)
# 绘制matplotlib绘制这些点, 以及点的类别
mp.figure('Simple Classification', facecolor='lightgray')
mp.title('Simple Classification', fontsize=16)
mp.xlabel('Feature1', fontsize=12)
mp.ylabel('Feature2', fontsize=12)
mp.tick_params(labelsize=10)
# 为网格点坐标矩阵填充颜色
mp.pcolormesh(grid_x, grid_y, grid_z, cmap='gray')
mp.scatter(x[:, 0], x[:, 1], c=y, cmap='brg', s=80)
mp.show()
多元分类
通过多个二元分类器结果多元分类的问题.
若拿到一组新的样本, 可以基于二元逻辑分类器 , 训练出一个模型, 判断属于A类别的概率. 在使用同样的方法训练出两个模型, 分别判断属于B, C类的概率, 最终选择概率最高的类别作为这组样本的分类结果.
案例: 基于逻辑分类模型的多元分类.
x = np.array([
[4, 7],
[3.5, 8],
[3.1, 6.2],
[0.5, 1],
[1, 2],
[1.2, 1.9],
[6, 2],
[5.7, 1.5],
[5.4, 2.2]
])
y = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2])
# 绘制类别边界线
l, r = x[:, 0].min() - 1, x[:, 0].max() + 1
b, t = x[:, 1].min() - 1, x[:, 1].max() + 1
n = 500
grid_x, grid_y = np.meshgrid(
np.linspace(l, r, n),
np.linspace(b, t, n)
)
m = lm.LogisticRegression(solver='liblinear', C=100)
m.fit(x, y)
samples = np.column_stack( # 将两个数组组合成为一个两列的二维数组
(grid_x.ravel(), grid_y.ravel())
)
grid_z = m.predict(samples)
grid_z = grid_z.reshape(grid_x.shape)
# 绘制matplotlib绘制这些点, 以及点的类别
mp.figure('Logistic Classification', facecolor='lightgray')
mp.title('Logistic Classification', fontsize=16)
mp.xlabel('Feature1', fontsize=12)
mp.ylabel('Feature2', fontsize=12)
mp.tick_params(labelsize=10)
# 为网格点坐标矩阵填充颜色
mp.pcolormesh(grid_x, grid_y, grid_z, cmap='gray')
mp.scatter(x[:, 0], x[:, 1], c=y, cmap='brg', s=80)
mp.show()
朴素贝叶斯分类
朴素贝叶斯分类是一种依据统计概率理论而实现的一种分类方式.
天气情况 | 穿衣风格 | 约女朋友 | ==> | 心情 |
---|---|---|---|---|
0(晴天) | 0(休闲) | 0(约了) | ==> | 0(高兴) |
0(晴天) | 1(风骚) | 1(没约) | ==> | 0(高兴) |
1(多云) | 1(风骚) | 0(约了) | ==> | 0(高兴) |
0(晴天) | 2(破旧) | 1(没约) | ==> | 1(郁闷) |
2(下雨) | 2(破旧) | 0(约了) | ==> | 0(高兴) |
… | … | … | ==> | … |
0(晴天) | 1(风骚) | 0(约了) | ==> | ? |
贝叶斯公式:
P(A|B) = P(A)P(B/A)/P(B)<=P(A, B)=P(A)P(B|A) = p(B)P(A|B)
P(A, B) <===> AB同时发生的概率
例如: 假设一个学校有60%男生和40%女生. 女生穿裤子的人数和穿裙子的人数相等, 所有男生都穿裤子, 一个人在远处随机看到了一个穿裤子的学生, 那么这个学生是女生的概率.
P(女) = 0.4
P(裤子|女) = 0.5
P(裤子) = 0.6 + 0.2 = 0.8
P(女生|裤子) = 0.4*0.5/0.8 = 0.25
由此可得, 统计总样本空间中晴天, 休闲, 没约并且高兴的概率, 然后再求总样本空间晴天, 休闲, 没约并且郁闷的概率, 取最大者作为分类结果.
P(晴天, 休闲, 没约, 高兴)
= P(晴天 | 休闲, 没约, 高兴) P(休闲, 没约, 高兴)
= P(晴天 | 休闲, 没约, 高兴) P(休闲 |
没约, 高兴)P(没约, 高兴)
= P(晴天 | 休闲, 没约, 高兴) P(休闲|没约, 高兴) P(没约 | 高兴) P(g)
(朴素: 条件独立, 特征值中间没有因果关系)
= P(晴天|高兴) P(休闲|高兴) P(没约|高兴)P(高兴)
朴素贝叶斯分类器相关API:
import sklearn.naive_bayes as nb
# 创建符合高斯分布的朴素贝叶斯分类器
model = nb.GaussianNB()
model.fit(x, y)
result = model.predict(samples)
案例: multiple1.txt
data = np.loadtxt('multiple1.txt', unpack=False, dtype='U20', delimiter=',')
x = np.array(data[:, :-1], dtype=float)
y = np.array(data[:, -1], dtype=float)
# 构建高斯朴素贝叶斯分类器
model = nb.GaussianNB()
model.fit(x, y)
l, r = x[:, 0].min() - 1, x[:, 0].max() + 1
b, t = x[:, 1].min() - 1, x[:, 1].max() + 1
n = 500
grid_x, grid_y = np.meshgrid(
np.linspace(l, r, n),
np.linspace(b, t, n)
)
samples = np.column_stack(
(grid_x.ravel(), grid_y.ravel())
)
grid_z = model.predict(samples)
grid_z = grid_z.reshape(grid_x.shape)
mp.figure('Naive Bayes Classification')
mp.title('Naive Bayes Classification', fontsize=16)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.pcolormesh(grid_x, grid_y, grid_z, cmap='gray')
mp.scatter(x[:, 0], x[:, 1], s=10, c=y, cmap='brg')
mp.show()
数据集的划分
对于分类问题, 训练集和测试集的划分不应该用整个样本空间的特定百分比作为训练数据, 而应该再其每一个类别的样本中抽取特定百分比作为训练数据
sklearn提供了数据集划分相关方法, 可以方便的划分训练集与测试数据, 相关API如下:
import sklearn.model_selection as ms
# train_test_split自动划分测试集与训练集
训练输入, 测试输入, 训练输出, 测试输出 = ms.train_test_split(
输入集,
输出集,
test_size=0.2, # 测试集占比
random_state=7
)
案例:
data = np.loadtxt('multiple1.txt', unpack=False, dtype='U20', delimiter=',')
x = np.array(data[:, :-1], dtype=float)
y = np.array(data[:, -1], dtype=float)
# 划分训练集与测试集
train_x, test_x, train_y, test_y = ms.train_test_split(x, y, test_size=0.25, random_state=7)
# 构建高斯朴素贝叶斯分类器
model = nb.GaussianNB()
model.fit(train_x, train_y)
l, r = x[:, 0].min() - 1, x[:, 0].max() + 1
b, t = x[:, 1].min() - 1, x[:, 1].max() + 1
n = 500
grid_x, grid_y = np.meshgrid(
np.linspace(l, r, n),
np.linspace(b, t, n)
)
samples = np.column_stack(
(grid_x.ravel(), grid_y.ravel())
)
grid_z = model.predict(samples)
grid_z = grid_z.reshape(grid_x.shape)
# 让模型预测测试集结果
pred_test_y = model.predict(test_x)
print('%.2f%%' % float((test_y == pred_test_y).sum()/pred_test_y.size*100))
mp.figure('Naive Bayes Classification')
mp.title('Naive Bayes Classification', fontsize=16)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.pcolormesh(grid_x, grid_y, grid_z, cmap='gray')
mp.scatter(test_x[:, 0], test_x[:, 1], s=10, c=test_y, cmap='brg')
mp.show()
交叉验证
由于数据集的划分有不确定性, 若随机划分的样本正好处于某类特殊样本, 那么得到的训练模型所预测的结果可信度将会受到质疑, 所以需要进行多次交叉验证. 把样本空间中所有的样本均分成n分, 使用不同的训练模型, 对不同的训练集进行测试, 输出指标得分.
sklearn提供了交叉验证相关API:
import sklearn.model_selection as ms
ms.cross_val_score(
模型, # 原始模型
输入集,
输出集,
cv=折叠树, # 折叠树, 做多少次交叉验证
scoring=指标名 # 'accuracy', 'precision_weighted'
)
交叉验证指标
- 精确度(accuracy): 分类正确的样本数/总样本数
- 查准率(precision_weighted): 针对每个类别, 预测正确的样本数/预测出来的样本数.
- 召回率(recall_weighted): 针对每一个类别, 预测正确的样本数/实际存在的样本数.
- f1得分(f1_weighted): 2 * 查准率 * 召回率 / (查准率 + 召回率)
案例:
data = np.loadtxt('multiple1.txt', unpack=False, dtype='U20', delimiter=',')
x = np.array(data[:, :-1], dtype=float)
y = np.array(data[:, -1], dtype=float)
# 划分训练集与测试集
train_x, test_x, train_y, test_y = ms.train_test_split(x, y, test_size=0.25, random_state=7)
# 构建高斯朴素贝叶斯分类器
model = nb.GaussianNB()
# 先做交叉验证, 看一下f1得分
ac = ms.cross_val_score(model, train_x, train_y, cv=5, scoring='accuracy')
pw = ms.cross_val_score(model, train_x, train_y, cv=5, scoring='precision_weighted')
rw = ms.cross_val_score(model, train_x, train_y, cv=5, scoring='recall_weighted')
f1 = ms.cross_val_score(model, train_x, train_y, cv=5, scoring='f1_weighted')
print(ac.mean(), pw.mean(), rw.mean(), f1.mean())
混淆矩阵
sklearn提供了混淆矩阵清晰的显示出每一类别的查准率, 召回率等验证结果. 每一行和每一列分别对应样本输出中的每一个类别, 行表示实际类别, 列表示预测类别.
- | A类别 | B类别 | C类别 |
---|---|---|---|
A类别 | 5 | 0 | 0 |
B类别 | 0 | 6 | 0 |
C类别 | 0 | 0 | 7 |
上述矩阵为理想的混淆矩阵. 不理想如下:
- | A类别 | B类别 | C类别 |
---|---|---|---|
A类别 | 3 | 1 | 1 |
B类别 | 0 | 4 | 2 |
C类别 | 0 | 0 | 7 |
获取模型分类结果的混淆矩阵相关API:
import sklearn.metrics as sm
m = sm.confusion_matrix(实际输出, 预测输出)
案例:
# 输出预测结果的混淆矩阵
m = sm.confusion_matrix(test_y, pred_test_y)
print(m)
mp.figure('M')
mp.imshow(m, cmap='gray')