本篇我会使用scikit-learn这个开源机器学习库来对iris数据集进行分类练习。python
我将分别使用两种不一样的scikit-learn
内置算法——Decision Tree(决策树)
和kNN(邻近算法)
,随后我也会尝试本身实现kNN
算法。目前为止,我仍是在机器学习的入门阶段,文章中暂不详细解释算法原理,若是想了解细节信息可自行搜索。git
scikit-learn
中预制了不少经典数据集,很是方便咱们本身练习用。使用方式也很容易:github
# 引入datasets from sklearn import datasets # 获取所需数据集 iris = datasets.load_iris()
load_iris
返回的结果有以下属性:算法
sepal length (cm)
, sepal width (cm)
, petal length (cm)
和 petal width (cm)
feature_names
中对应的值label
值),其值为[0,1,2]监督学习
能够用一个简单的数学公式来表明:数组
y = f(X)app
按上一篇中的相关术语描述就是已知X(features)
,经过方法f(classifier)
求y(label)
。机器学习
按照这个思路,我将iris
数据分离为:ide
# X = features X = iris.data # y = label y = iris.target
那如何来使用数据呢?由于只有150行数据,因此为了验证算法的正确性,须要将数据分红两部分:训练数据
和测试数据
,很幸运的是scikit-learn
也提供了方便分离数据的方法train_test_split
,我将数据分离成60%
(即90条数据)用于训练,40%
(即60条数据)用于测试,代码以下:函数
from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.6)
上一篇中已经用过决策树,使用决策树的代码简单以下:性能
# Decision tree classifier # 生成决策树 my_classification = tree.DecisionTreeClassifier() # 训练 my_classification.fit(X_train, y_train) # 预测 predictions = my_classification.predict(X_test)
经过决策树算法,最终获得的模型的准确率有多少呢?这个时候可使用scikit-learn
的accuracy_score
方法:
# 得到预测准确率 print(accuracy_score(y_test, predictions))
因为train_test_split是随机切分数据,所以最终跑出来的准确率不是一个固定值
kNN算法就是选取k个最近邻居来归类样本值的方法,这是最简单的一种分类算法,固然缺点也很明显,必须循环计算测试样本值和全部的样本之间的距离,运行效率比较低。
在选用kNN算法的时候,k值最好是奇数
,偶数值会形成没法归到惟一类的状况(属于不一样分类的几率正好相等)。
只需在上述分离数据以后,将决策树算法的代码替换为:
# N neighbors classifier # 生成kNN my_classification2 = KNeighborsClassifier(n_neighbors=5) # 训练 my_classification2.fit(X_train, y_train) # 预测 predictions2 = my_classification2.predict(X_test) # 得到预测准确率 print(accuracy_score(y_test, predictions2))
因为train_test_split是随机切分数据,所以最终跑出来的准确率不是一个固定值。并且因为算法不一样,即使是相同的数据,跑出来的准确率也和决策树跑出来的不一样。
基本思路是沿用上述内置kNN算法的代码,从新实现KNeighborsClassifier
,称之为MyKNN
好了。除了初始化函数以外,还须要fit
和predict
这两个方法,而且方法签名和原先的保持一致,因此MyKNN
类的基本结构以下:
class MyKNN: def __init__(self, n_neighbors=5): pass def fit(self, X_train, y_train): pass def predict(self, X_test): pass
初始化方法仅需初始化几个参数以便后续使用:
def __init__(self, n_neighbors=5): self.n_neighbors = n_neighbors self.X_train = None self.y_train = None
在这里我简单处理该方法,因为原先fit方法包含了X和y两个参数,所以沿用该方法签名,这样就不须要改动其余代码了:
def fit(self, X_train, y_train): self.X_train = X_train self.y_train = y_train
我须要在该方法中遍历计算测试数据和训练数据之间的距离,两点之间的距离可使用欧几里得公式,所以须要先定义一个外部方法my_euclidean
:
def my_euclidean(a, b): return distance.euclidean(a, b)
我须要计算当前测试数据与K个最近距离的训练数据之间的值,而后看一下这K个数据中,最多的分类是哪一种,则可认为测试数据也属于该种分类(几率最高)。所以先定义一个私有方法__closest
:
def __closest(self, row): all_labels = [] for i in range(0, len(self.X_train)): dist = my_euclidean(row, self.X_train[i]) # 获取k个最近距离的邻居,格式为(distance, index)的tuple集合 all_labels = self.__append_neighbors(all_labels, (dist, i)) # 将k个距离最近的邻居,映射为label的集合 nearest_ones = np.array([self.y_train[idx] for val, idx in all_labels]) # 使用numpy的unique方法,分组计算label的惟一值及其对应的值第一次出现的index和值的计数 # 例: elements = [1, 2], elements_index = [3,0], elements_count = [1, 4] 这个结合表示: # elements = [1, 2] : 出现了1和2两种类型的数据 # elements_index = [3,0] : 1第一次出现的index是3, 2第一次出现的index是0 # elements_count = [1, 4] : 1共出现了1次, 2共出现了4次 elements, elements_index, elements_count = np.unique(nearest_ones, return_counts=True, return_index=True) # 返回最大可能性的那种类型的label值 return elements[list(elements_count).index(max(elements_count))]
为了提高性能,我定义了__append_neighbors
方法,该方法将当前距离-序号
的tuple加入到数组中并按升序排序,最终只截取前k个值,能够用python的特性很容易实现该逻辑:
def __append_neighbors(self, arr, item): if len(arr) <= self.n_neighbors: arr.append(item) return sorted(arr, key=lambda tup: tup[0])[:self.n_neighbors]
短短几行代码就实现了本身的kNN
算法,我本地跑下来的准确率在95%以上。
须要完整代码能够在个人GitHub上找到。