Sklearn+Flask实现决策树模型及部署
sklearn和决策树
决策树
决策树是机器学习的一种算法,简要原理分析见上篇。
sklearn
sklearn是python的一个机器学习框架,其中包含了很多机器学习算法的实现。sklearn.tree模块提供了决策树模型,用于解决分类问题和回归问题。本次模型的实现是使用的DecisionTreeClassifier来构建决策树。
对于sklearn.tree,可以看官网:https://scikit-learn.org/stable/modules/classes.html#module-sklearn.tree
对于sklearn.tree.DecisionTreeClassifier的参数说明,一共12个参数
1.criterion:特征选择标准,可选参数,默认是gini,可以设置为entropy。gini是基尼不纯度,是将来自集合的某种结果随机应用于某一数据项的预期误差率,是一种基于统计的思想。entropy是香农熵,也就是上篇文章讲过的内容,是一种基于信息论的思想。Sklearn把gini设为默认参数,应该也是做了相应的斟酌的,精度也许更高些?ID3算法使用的是entropy,CART算法使用的则是gini。
2.splitter:特征划分点选择标准,可选参数,默认是best,可以设置为random。每个结点的选择策略。best参数是根据算法选择最佳的切分特征,例如gini、entropy。random随机的在部分划分点中找局部最优的划分点。默认的"best"适合样本量不大的时候,而如果样本数据量非常大,此时决策树构建推荐"random"。
3.max_features:划分时考虑的最大特征数,可选参数,默认是None。寻找最佳切分时考虑的最大特征数(n_features为总共的特征数),有如下6种情况:
如果max_features是整型的数,则考虑max_features个特征;
如果max_features是浮点型的数,则考虑int(max_features * n_features)个特征;
如果max_features设为auto,那么max_features = sqrt(n_features);
如果max_features设为sqrt,那么max_featrues = sqrt(n_features),跟auto一样;
如果max_features设为log2,那么max_features = log2(n_features);
如果max_features设为None,那么max_features = n_features,也就是所有特征都用。
一般来说,如果样本特征数不多,比如小于50,我们用默认的"None"就可以了,如果特征数非常多,我们可以灵活使用刚才描述的其他取值来控制划分时考虑的最大特征数,以控制决策树的生成时间。
4.max_depth:决策树最大深,可选参数,默认是None。这个参数是这是树的层数的。层数的概念就是,比如在贷款的例子中,决策树的层数是2层。如果这个参数设置为None,那么决策树在建立子树的时候不会限制子树的深度。一般来说,数据少或者特征少的时候可以不管这个值。或者如果设置了min_samples_slipt参数,那么直到少于min_smaples_split个样本为止。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,具体的取值取决于数据的分布。常用的可以取值10-100之间。
5.min_samples_split:内部节点再划分所需最小样本数,可选参数,默认是2。这个值限制了子树继续划分的条件。如果min_samples_split为整数,那么在切分内部结点的时候,min_samples_split作为最小的样本数,也就是说,如果样本已经少于min_samples_split个样本,则停止继续切分。如果min_samples_split为浮点数,那么min_samples_split就是一个百分比,ceil(min_samples_split * n_samples),数是向上取整的。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
6.min_samples_leaf:叶子节点最少样本数,可选参数,默认是1。这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。叶结点需要最少的样本数,也就是最后到叶结点,需要多少个样本才能算一个叶结点。如果设置为1,哪怕这个类别只有1个样本,决策树也会构建出来。如果min_samples_leaf是整数,那么min_samples_leaf作为最小的样本数。如果是浮点数,那么min_samples_leaf就是一个百分比,同上,celi(min_samples_leaf * n_samples),数是向上取整的。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
7.min_weight_fraction_leaf:叶子节点最小的样本权重和,可选参数,默认是0。这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。
8.max_leaf_nodes:最大叶子节点数,可选参数,默认是None。通过限制最大叶子节点数,可以防止过拟合。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制,具体的值可以通过交叉验证得到。
9.class_weight:类别权重,可选参数,默认是None,也可以字典、字典列表、balanced。指定样本各类别的的权重,主要是为了防止训练集某些类别的样本过多,导致训练的决策树过于偏向这些类别。类别的权重可以通过{class_label:weight}这样的格式给出,这里可以自己指定各个样本的权重,或者用balanced,如果使用balanced,则算法会自己计算权重,样本量少的类别所对应的样本权重会高。当然,如果你的样本类别分布没有明显的偏倚,则可以不管这个参数,选择默认的None。
10.random_state:可选参数,默认是None。随机数种子。如果是证书,那么random_state会作为随机数生成器的随机数种子。随机数种子,如果没有设置随机数,随机出来的数与当前系统时间有关,每个时刻都是不同的。如果设置了随机数种子,那么相同随机数种子,不同时刻产生的随机数也是相同的。如果是RandomState instance,那么random_state是随机数生成器。如果为None,则随机数生成器使用np.random。
11.min_impurity_split:节点划分最小不纯度,可选参数,默认是1e-7。这是个阈值,这个值限制了决策树的增长,如果某节点的不纯度(基尼系数,信息增益,均方差,绝对差)小于这个阈值,则该节点不再生成子节点。即为叶子节点。
12.presort:数据是否预排序,可选参数,默认为False,这个值是布尔值,默认是False不排序。一般来说,如果样本量少或者限制了一个深度很小的决策树,设置为true可以让划分点选择更加快,决策树建立的更加快。如果样本量太大的话,反而没有什么好处。问题是样本量少的时候,我速度本来就不慢。所以这个值一般懒得理它就可以了。
sklearn.tree.DecisionTreeClassifier()提供了一些方法供我们使用,如下所示:
详细的可以看sklearn官网给出的解释:
sklearn.tree.DecisionTreeClassifier — scikit-learn 1.0.2 documentation
代码部分
由于我们需要实现API的编写,模型的训练并不是我们关心的重点。这里分为两部分,一部分是对于flask的代码编写,一部分是对于sklearn模型的训练。其中flask代码会调用sklearn的训练模型,给模型对应的参数,模型再将预测结果返回给flask,flask再进行呈现。
flask部分代码
from flask import Flask,request
import sklearnModel
app = Flask(__name__)
@app.route("/api/classifier")
def getClassifierData():
# 获取参数
data1 = request.args.get("data1")
data2 = request.args.get("data2")
data3 = request.args.get("data3")
data4 = request.args.get("data4")
# 调用预测
predict = sklearnModel.iris(data1,data2,data3,data4)
return predict
if __name__ == "__main__":
app.run(host="localhost",port=5000,debug=True)
slearn部分代码
from sklearn import tree
import numpy as np
from sklearn import datasets
def iris(data1,data2,data3,data4):
#载入数据集
iris=datasets.load_iris()
iris_data=iris['data']
iris_label=iris['target']
iris_target_name=iris['target_names']
X=np.array(iris_data)
Y=np.array(iris_label)
#训练
clf=tree.DecisionTreeClassifier(max_depth=3)
clf.fit(X,Y)
#这里预测当前输入的值的所属分类
predict = '类别是' +" " + iris_target_name[clf.predict([[data1,data2,data3,data4]])[0]]
# print('类别是',iris_target_name[clf.predict([[data1,data2,data3,data4]])[0]])
return predict
在服务器上运行
需要先安装环境,这里是在虚拟环境中跑的代码,所以需要先在虚拟环境中安装对应的环境,包括sklearn、pandas等。安装完毕后,运行flask代码,就可以实现在服务器上的运行。
稍微美观一点
代码部分
添加一点html代码,让它美观一点。包括了变量输入和提交。图片的读取和返回。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>鸢尾花分类网页</title>
</head>
<body>
<h1>欢迎来到鸢尾花分类页面!</h1>
<form action="http://localhost:5000/api/classifier" method="GET">
<p>花萼长度:<input type="text" name="data1"/></p>
<p>花萼宽度:<input type="text" name="data2"/></p>
<p>花瓣长度:<input type="text" name="data3"/></p>
<p>花瓣宽度:<input type="text" name="data4"/></p>
<p><input type="submit" value="submit"/></p>
</form>
</body>
</html>
judge.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>自动分类网页</title>
</head>
<body>
<h1> {{ data.data }} !</h1>
<img src="data:image/png;base64, {{ data.image }}">
</body>
</html>
flask代码对应进行修改
@app.route('/')
@app.route('/index')
def hello():
return render_template('index.html')
@app.route("/api/classifier")
def getClassifierData():
data1 = request.args.get("data1")
data2 = request.args.get("data2")
data3 = request.args.get("data3")
data4 = request.args.get("data4")
predict = sklearnModel.iris(data1,data2,data3,data4)
data = {}
img_stream = ''
with open(r'./1.jpg', 'rb') as img_f:
img_stream = img_f.read()
img_stream = base64.b64encode(img_stream).decode()
data = {"data":predict,"image":img_stream}
return render_template('judge.html',data=data)