Tour of Machine Learning Algorithms(5) 常见算法优缺点
前文传送
文章结构:
什么是感知器分类算法
在Python中实现感知器学习算法
在iris(鸢尾花)数据集上训练一个感知器模型
- 自适应线性神经元和融合学习
使用梯度下降方法来最小化损失函数
在Python中实现一个自适应的线性神经元
什么是感知器分类算法
设想我们改变逻辑回归算法,“迫使”它只能输出-1或1抑或其他定值。在这种情况下,之前的逻辑函数g就会变成阈值函数sign:
如果我们令假设为hθ(x)=g(θTx)hθ(x)=g(θTx),将其带入之前的迭代法中:
至此我们就得出了感知器学习算法。简单地来说,感知器学习算法是神经网络中的一个概念,单层感知器是最简单的神经网络,输入层和输出层直接相连。
每一个输入端和其上的权值相乘,然后将这些乘积相加得到乘积和,这个结果与阈值相比较(一般为0),若大于阈值输出端就取1,反之,输出端取-1。
初始权重向量W=[0,0,0],更新公式W(i)=W(i)+ΔW(i);ΔW(i)=η(y-y’)X(i);
η:学习率,介于[0,1]之间
y:输入样本的正确分类
y’:感知器计算出来的分类
通过上面公式不断更新权值,直到达到分类要求。
初始化权重向量W,与输入向量做点乘,将结果与阈值作比较,得到分类结果1或-1。
在Python中实现感知器学习算法
下面直接贴上实现代码:
1 | import numpy as np |
特别说明:
学习速率η(eta)只有在权重(一般取值0或者很小的数)为非零值的时候,才会对分类结果产生作用。如果所有的权重都初始化为0,学习速率参数eta只影响权重向量的大小,而不影响其方向,为了使学习速率影响分类结果,权重需要初始化为非零值。需要更改的代码中的相应行在下面突出显示:1
2
3
4
5
6
7
8def __init__(self, eta=0.01, n_iter=50, random_seed=1): # add random_seed=1
...
self.random_seed = random_seed # add this line
def fit(self, X, y):
...
# self.w_ = np.zeros(1 + X.shape[1]) ## remove this line
rgen = np.random.RandomState(self.random_seed) # add this line
self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape[1]) # add this line
在iris(鸢尾)数据集上训练一个感知器模型
读取iris数据集1
2
3
4
5
6
7
8
9
10import pandas as pd
import collections
df = pd.read_csv('https://archive.ics.uci.edu/ml/'
'machine-learning-databases/iris/iris.data', header=None)
print (df.head())
print ("\n")
print (df.describe())
print ("\n")
print (collections.Counter(df[4]))
output:
可视化iris数据1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
# 为了显示中文(这里是Mac的解决方法,其他的大家可以去百度一下)
from matplotlib.font_manager import FontProperties
font = FontProperties(fname='/System/Library/Fonts/STHeiti Light.ttc')
# 选择 setosa and versicolor类型的花
y = df.iloc[0:100, 4].values
y = np.where(y == 'Iris-setosa', -1, 1)
# 提取它们的特征 (sepal length and petal length)
X = df.iloc[0:100, [0, 2]].values
# 可视化数据,因为数据有经过处理,总共150行数据,1-50行是setosa花,51-100是versicolor花,101-150是virginica花
plt.scatter(X[:50, 0], X[:50, 1],
color='red', marker='o', label='setosa')
plt.scatter(X[50:100, 0], X[50:100, 1],
color='blue', marker='x', label='versicolor')
plt.xlabel('sepal 长度 [cm]',FontProperties=font,fontsize=14)
plt.ylabel('petal 长度 [cm]',FontProperties=font,fontsize=14)
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()
output:
训练感知器模型1
2
3
4
5
6
7
8
9
10
11# Perceptron是我们前面定义的感知器算法函数,这里就直接调用就好
ppn = Perceptron(eta=0.1, n_iter=10)
ppn.fit(X, y)
plt.plot(range(1, len(ppn.errors_) + 1), ppn.errors_, marker='o')
plt.xlabel('迭代次数',FontProperties=font,fontsize=14)
plt.ylabel('权重更新次数(错误次数)',FontProperties=font,fontsize=14)
plt.tight_layout()
plt.show()
output:
绘制函数决策区域1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35from matplotlib.colors import ListedColormap
def plot_decision_regions(X, y, classifier, resolution=0.02):
# setup marker generator and color map
markers = ('s', 'x', 'o', '^', 'v')
colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
cmap = ListedColormap(colors[:len(np.unique(y))])
# plot the decision surface
x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
np.arange(x2_min, x2_max, resolution))
Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
Z = Z.reshape(xx1.shape)
plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
plt.xlim(xx1.min(), xx1.max())
plt.ylim(xx2.min(), xx2.max())
# plot class samples
for idx, cl in enumerate(np.unique(y)):
plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1],
alpha=0.8, c=cmap(idx),
edgecolor='black',
marker=markers[idx],
label=cl)
plot_decision_regions(X, y, classifier=ppn)
plt.xlabel('sepal 长度 [cm]',FontProperties=font,fontsize=14)
plt.ylabel('petal 长度 [cm]',FontProperties=font,fontsize=14)
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()
output:
自适应线性神经元和融合学习
使用梯度下降方法来最小化损失函数
梯度下降的方法十分常见,具体的了解可以参考附录的文章[2],如今,梯度下降主要用于在神经网络模型中进行权重更新,即在一个方向上更新和调整模型的参数,来最小化损失函数。
图:梯度下降原理过程演示
在Python中实现一个自适应的线性神经元
先贴上定义的python函数,1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69# 定义神经元函数
class AdalineGD(object):
"""ADAptive LInear NEuron classifier.
Parameters
------------
eta : float
Learning rate (between 0.0 and 1.0)
n_iter : int
Passes over the training dataset.
Attributes
-----------
w_ : 1d-array
Weights after fitting.
cost_ : list
Sum-of-squares cost function value in each epoch.
"""
def __init__(self, eta=0.01, n_iter=50):
self.eta = eta
self.n_iter = n_iter
def fit(self, X, y):
""" Fit training data.
Parameters
----------
X : {array-like}, shape = [n_samples, n_features]
Training vectors, where n_samples is the number of samples and
n_features is the number of features.
y : array-like, shape = [n_samples]
Target values.
Returns
-------
self : object
"""
self.w_ = np.zeros(1 + X.shape[1])
self.cost_ = []
for i in range(self.n_iter):
net_input = self.net_input(X)
# Please note that the "activation" method has no effect
# in the code since it is simply an identity function. We
# could write `output = self.net_input(X)` directly instead.
# The purpose of the activation is more conceptual, i.e.,
# in the case of logistic regression, we could change it to
# a sigmoid function to implement a logistic regression classifier.
output = self.activation(X)
errors = (y - output)
self.w_[1:] += self.eta * X.T.dot(errors)
self.w_[0] += self.eta * errors.sum()
cost = (errors**2).sum() / 2.0
self.cost_.append(cost)
return self
def net_input(self, X):
"""Calculate net input"""
return np.dot(X, self.w_[1:]) + self.w_[0]
def activation(self, X):
"""Compute linear activation"""
return self.net_input(X)
def predict(self, X):
"""Return class label after unit step"""
return np.where(self.activation(X) >= 0.0, 1, -1)
查看不同学习率下的错误率随迭代次数的变化情况:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(8, 4))
# 可视化W调整的过程中,错误率随迭代次数的变化
ada1 = AdalineGD(n_iter=10, eta=0.01).fit(X, y)
ax[0].plot(range(1, len(ada1.cost_) + 1), np.log10(ada1.cost_), marker='o')
ax[0].set_xlabel('Epochs')
ax[0].set_ylabel('log(Sum-squared-error)')
ax[0].set_title('Adaline - Learning rate 0.01')
ada2 = AdalineGD(n_iter=10, eta=0.0001).fit(X, y)
ax[1].plot(range(1, len(ada2.cost_) + 1), ada2.cost_, marker='o')
ax[1].set_xlabel('Epochs')
ax[1].set_ylabel('Sum-squared-error')
ax[1].set_title('Adaline - Learning rate 0.0001')
plt.tight_layout()
plt.show()
output:
iris数据的应用情况:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21# 标准化特征
X_std = np.copy(X)
X_std[:, 0] = (X[:, 0] - X[:, 0].mean()) / X[:, 0].std()
X_std[:, 1] = (X[:, 1] - X[:, 1].mean()) / X[:, 1].std()
# 调用函数开始训练
ada = AdalineGD(n_iter=15, eta=0.01)
ada.fit(X_std, y)
# 绘制效果
plot_decision_regions(X_std, y, classifier=ada)
plt.title('Adaline - Gradient Descent')
plt.xlabel('sepal length [standardized]')
plt.ylabel('petal length [standardized]')
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()
# 可视化W调整的过程中,错误率随迭代次数的变化
plt.plot(range(1, len(ada.cost_) + 1), ada.cost_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Sum-squared-error')
plt.tight_layout()
plt.show()
output:
参考资料
1)机器学习系列:感知器
2)机器学习入门系列04,Gradient Descent(梯度下降法)
3)一文看懂各种神经网络优化算法:从梯度下降到Adam方法
4)机器学习与神经网络(三):自适应线性神经元的介绍和Python代码实现
5)《Training Machine Learning Algorithms for Classification》