Python机器学习之-决策树篇
决策树作为机器学习常用算法,更是作为集成学习的基础,不可谓不重要,在这里简单介绍决策树的原理及应用
一:决策树原理
顾名思义决策树是一个树状结构(由于算法的不同也决定了是二叉树还是多叉树,后面会详细描述),从根节点走向叶子节点,实际上决策树就相当于是if-else,便于理解,下图形象的展示了决策树的学习过程,从上到下的节点顺序代表了特征对结果的重要性顺序
二:决策树的三种算法
在介绍决策树的是三种算法之前,简单接受一下熵和信息增益以及基尼系数
熵:熵是描述数据的不确定程度,熵越大代表数据的混乱程度越大,对决策树的作用就越小
信息增益:信息增益越大代表这个特征分类的能力越强,对结果的影响越大,也可以理解为熵的变化越小,对决策树的作用越大
基尼系数:基尼系数代表了模型的不纯度,基尼系数越小,纯度越高,特征越好。这和信息增益(比)相反。
1.ID3算法
ID3算法是基于信息增益划分的算法(构成多叉树)
1.1 ID3算法的缺点
(1)不支持连续特征
(2)采用信息增益大的特征优先建立决策树的节点。在相同条件下,取值比较多的特征比取值少的特征信息增益大。
(3)不支持缺失值处理
(4)没有应对过拟合的策略
2.C4.5算法
C4.5算法是基于信息增益比的算法,并且改进了ID3的各项缺点(构成多叉树)
2.1 C4.5算法的缺点
C4.5缺点
虽然C4.5改善了ID3的各项缺点,但是又造成了新的缺点
(1)剪枝的算法有非常多,C4.5的剪枝方法有优化的空间
(2)C4.5生成的是多叉树,很多时候,在计算机中二叉树模型会比多叉树运算效率高。如果采用二叉树,可以提高效率
(3)C4.5只能用于分类
(4)C4.5使用了熵模型,里面有大量的耗时的对数运算,如果是连续值还有大量的排序运算
3.CART算法
CART算法是基于基尼系数的算法,对C4.5算法进行了优化,不仅可以用于分类也可以用于回归,还提供了剪枝的策略,绝大多数时候都是选择CART算法作为决策树算法
决策树的优缺点
决策树的优势
1.决策树可以用于回归和分类,用途广泛且十分常用
2.决策树简单易理解
3.决策树可使用可视化模型,画出特征重要性图
4.下次使用决策树可直接调用,不需再次训练
决策树的劣势
1.决策树不稳定,极小的变化也会给结果带来很大的变化
2.决策树易造成过拟合(使用随机森林就不会有这个问题了)
3.决策树的变现相对于集成学习来说,效果并不是特别理想
4.决策树对大数据量处理速度会很慢,决策树会把数据加载到内存中,训练速度会很感人
加州房价决策树
数据源来自加州房价
import warnings
import pandas as pd
import numpy as np
from sklearn import tree
import pydotplus
from sklearn.decomposition import PCA
from sklearn.linear_model import LinearRegression, LassoCV, RidgeCV
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import MinMaxScaler
from sklearn.tree import DecisionTreeRegressor
import matplotlib as mpl
import matplotlib.pyplot as plt
def notEmpty(s):
return s != ''
# mpl包中解决中文显示问题
mpl.rcParams['font.sans-serif']=[u'simHei']
mpl.rcParams['axes.unicode_minus']=False
# 拦截异常
warnings.filterwarnings(action = 'ignore')
# 取列名
names = ['CRIM','ZN', 'INDUS','CHAS','NOX','RM','AGE','DIS','RAD','TAX','PTRATIO','B','LSTAT']
# 指定路径
path = "housing.data"
# 由于数据文件格式不统一,所以读取的时候,先按照一行一个字段属性读取数据,然后再按照每行数据进行处理
fd = pd.read_csv(path, header=None)
data = np.empty((len(fd), 14))
# enumerate为python中内置函数 作用是将 一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,
# 同时列出数据和数据下标,一般用在 for 循环当中
for i, d in enumerate(fd.values):
d = map(float, filter(notEmpty, d[0].split(' ')))
data[i] = list(d)
x, y = np.split(data, (13,), axis=1)
y = y.ravel()
print ("样本数据量:%d, 特征个数:%d" % x.shape)
print ("target样本数据量:%d" % y.shape[0])
#数据的分割
x_train1, x_test1, y_train1, y_test1 = train_test_split(x, y, train_size=0.8, random_state=14)
x_train, x_test, y_train, y_test = x_train1, x_test1, y_train1, y_test1
print ("训练数据集样本数目:%d, 测试数据集样本数目:%d" % (x_train.shape[0], x_test.shape[0]))
#标准化
ss = MinMaxScaler()
x_train = ss.fit_transform(x_train, y_train)
x_test = ss.transform(x_test)
print ("原始数据各个特征属性的调整最小值:",ss.min_)
print ("原始数据各个特征属性的缩放数据值:",ss.scale_)
#构建决策树模型(回归)
model = DecisionTreeRegressor(criterion='mae',max_depth=7)
#模型训练
model.fit(x_train, y_train)
#模型预测
y_test_hat = model.predict(x_test)
#评估模型
score = model.score(x_test, y_test)
print ("Score:", score)
#
# #构建线性回归
lr = LinearRegression()
lr.fit(x_train,y_train)
lr_y_test_hat = lr.predict(x_test)
lr_score = lr.score(x_test, y_test)
print ("lr:", lr_score)
# #构建lasso
lasso = LassoCV(alphas=np.logspace(-3,1,20))
lasso.fit(x_train, y_train)
lasso_y_test_hat = lasso.predict(x_test)
lasso_score = lasso.score(x_test, y_test)
print ("lasso:", lasso_score)
# #构建岭回归
ridge = RidgeCV(alphas=np.logspace(-3,1,20))
ridge.fit(x_train, y_train)
ridge_y_test_hat = ridge.predict(x_test)
ridge_score = ridge.score(x_test, y_test)
print ("ridge:", ridge_score)
# 7. 画图
plt.figure(figsize=(12,6), facecolor='w')
ln_x_test = range(len(x_test))
plt.plot(ln_x_test, y_test, ls="-", lw=2, label=u'实际值')
plt.plot(ln_x_test, lr_y_test_hat, ls="-", lw=2, label=u'Linear回归,$R^2$=%.3f' % lr_score)
plt.plot(ln_x_test, lasso_y_test_hat, ls="-", lw=2, label=u'Lasso回归,$R^2$=%.3f' % lasso_score)
plt.plot(ln_x_test, ridge_y_test_hat, ls="-", lw=2, label=u'Ridge回归,$R^2$=%.3f' % ridge_score)
plt.plot(ln_x_test, y_test_hat, ls="-", lw=2, label=u'回归决策树预测值,$R^2$=%.3f' % score)
plt.xlabel(u'数据编码')
plt.ylabel(u'租赁价格')
plt.legend(loc = 'lower right')
plt.grid(True)
plt.title(u'波士顿房屋租赁数据预测')
plt.show()
# 参数优化
pipes = [
Pipeline([
('mms', MinMaxScaler()), ## 归一化操作
('pca', PCA()), ## 降纬
('decision', DecisionTreeRegressor(criterion='mse'))
]),
Pipeline([
('mms', MinMaxScaler()),
('decision', DecisionTreeRegressor(criterion='mse'))
]),
Pipeline([
('decision', DecisionTreeRegressor(criterion='mse'))
])
]
# 参数
parameters = [
{
"pca__n_components": [0.25, 0.5, 0.75, 1],
"decision__max_depth": np.linspace(1, 20, 20).astype(np.int8)
},
{
"decision__max_depth": np.linspace(1, 20, 20).astype(np.int8)
},
{
"decision__max_depth": np.linspace(1, 20, 20).astype(np.int8)
}
]
# 获取数据
x_train2, x_test2, y_train2, y_test2 = x_train1, x_test1, y_train1, y_test1
for t in range(3):
pipe = pipes[t]
gscv = GridSearchCV(pipe, param_grid=parameters[t])
gscv.fit(x_train2, y_train2)
print(t, "score值:", gscv.best_score_, "最优参数列表:", gscv.best_params_)
#使用最优参数看看正确率
mms_best = MinMaxScaler()
decision3 = DecisionTreeRegressor(criterion='mse', max_depth=4)
x_train3, x_test3, y_train3, y_test3 = x_train1, x_test1, y_train1, y_test1
x_train3 = mms_best.fit_transform(x_train3, y_train3)
x_test3 = mms_best.transform(x_test3)
decision3.fit(x_train3, y_train3)
print ("正确率:", decision3.score(x_test3, y_test3))
# 查看各个不同深度的错误率
x_train4, x_test4, y_train4, y_test4 = x_train1, x_test1, y_train1, y_test1
depths = np.arange(1, 20)
err_list = []
for d in depths:
clf = DecisionTreeRegressor(criterion='mse', max_depth=d)
clf.fit(x_train4, y_train4)
score1 = clf.score(x_test4, y_test4)
err = 1 - score1
err_list.append(err)
print("%d深度,正确率%.5f" % (d, score1))
## 画图
plt.figure(facecolor='w')
plt.plot(depths, err_list, 'ro-', lw=3)
plt.xlabel(u'决策树深度', fontsize=16)
plt.ylabel(u'错误率', fontsize=16)
plt.grid(True)
plt.title(u'决策树层次太多导致的拟合问题(欠拟合和过拟合)', fontsize=18)
plt.show()
# 方式三:直接生成图片
dot_data = tree.export_graphviz(decision3, out_file=None,
filled=True, rounded=True,
special_characters=True)
graph = pydotplus.graph_from_dot_data(dot_data)
graph.write_pdf("housing.pdf")