系列文章目录
【python tensorflow 人工智能】一元线性回归(从理论到实践,含偏导数的详细推理,初学者都能看懂)
实战应用 (引子)
小升初后,作为临时班长的你拿到了一打体检记录表,老师需要你男生和女生的体检单分开。体检单上写了姓名,体重,身高和性别 4 列数据。但是粗心的你,分完其中一部分类后其他的放在桌上不小心被墨水打湿了,性别这一列几乎完全不见了,身高和体重依稀还能分辨出来,但可能会有一些错误。老师安慰你说,性别这一列不重要,分好类后就没用了,但是在仅有的条件下,如何对剩下的进行分类呢?
问题分析
情景分析
聪明的你很快想到,男生和女生的身高体重大体上是有区别的,凭借之前分好类的数据,大体可以估计出其他数据的分类。但是具体怎么找到这个关系呢?
画图分析
为了方便(真实的身高体重表不好捏造),我们把真个问题抽象一下: 现在有两个维度的数据,需要根据这两个维度的数据对以下数据集进行分类。
在这里给出一个获得模拟数据的网址:https://www.gairuo.com/file/data/dataset/iris.data
不用下载,后面的程序会自动获取,但是大家可以先看看。我们以最后一列为分类标签,第 1 列 和 第 3 列 作为分类参数。
下面给出以上数据的图表
这里不给出需要预测的数据,大家可以自行找点预测,当然也可以在训练的时候不使用所有数据,留一部分做预测。
算法原理
人工神经元
先介绍以下,人工神经元。
为了设计人工智能,人们尝试模仿生物神经元,神经元是大脑中连接起来参与化学和电信号处理与传输的神经细胞,麦库洛和皮兹(MCP)把神经细胞描述为带有二进制输出的简单逻辑门,多个信号到达树突,然后整合到细胞体,并当信号累计到一定的阈值时,输出信号将通过轴突。
弗兰克·罗森布拉特首先提出了基于MCP神经元模型的感知器学习规则概念。
具体的说,模型如下:
感知器模型
模型就是这样,基于此,可以将人工神经元逻辑放于二元分类场景,将两类分为
1
1
1 正类,
−
1
-1
−1 负类,决策函数(
ϕ
(
z
)
\phi(z)
ϕ(z)),输入值
x
x
x, 权重
w
w
w, 设净输入为
z
=
w
1
x
1
+
w
2
x
2
+
w
3
x
3
+
⋯
+
w
m
x
m
z = w_1x_1 + w_2x_2 + w_3x_3 + \cdots + w_mx_m
z=w1x1+w2x2+w3x3+⋯+wmxm
x
=
[
x
1
x
2
…
x
m
]
,
w
=
[
w
1
w
2
…
w
m
]
x = \left[ \begin{matrix} x_1 \\ x_2 \\ \ldots \\ x_m \end{matrix} \right] ,w = \left[ \begin{matrix} w_1 \\ w_2 \\ \ldots \\ w_m \end{matrix} \right]
x=
x1x2…xm
,w=
w1w2…wm
设阈值为
θ
\theta
θ ,因为输入的样本
x
x
x 不止一个,所以我们设第
i
i
i 个样本的
x
x
x 设为
x
(
i
)
x^{(i)}
x(i),分类结果如下 (
ϕ
(
z
)
\phi(z)
ϕ(z) 是单位阶跃函数的变体)
ϕ
(
z
)
=
{
1
,
i
f
z
≥
θ
−
1
,
i
f
o
t
h
e
r
w
i
s
e
\phi(z) = \begin{cases} 1, if \ z \geq \theta \\ \\ -1,if \ otherwise \end{cases}
ϕ(z)=⎩
⎨
⎧1,if z≥θ−1,if otherwise
单位阶跃函数:
u ( t ) = { 0 f o r t < 0 1 f o r t > 0 u(t) = \begin{cases} 0 \ for \ t < 0 \\ \\ 1 \ for \ t > 0 \end{cases} u(t)=⎩ ⎨ ⎧0 for t<01 for t>0
它可以用来描述一个系统的输入和输出之间的关系。
但是
≥
θ
\ge \theta
≥θ 毕竟不好看,也不好用,不如
0
0
0 来的好,所以我们给他变一下型,增添一个
w
0
=
−
θ
,
x
0
=
1
w_0 = - \theta,x_0 = 1
w0=−θ,x0=1 这样就好了:
z
=
w
0
x
0
+
w
1
x
1
+
⋯
+
w
m
x
m
=
w
T
x
z = w_0x_0 + w_1x_1 + \cdots + w_mx_m = w^Tx
z=w0x0+w1x1+⋯+wmxm=wTx
这样
ϕ
(
z
)
\phi(z)
ϕ(z) 将变为:
ϕ
(
z
)
=
{
1
,
i
f
z
≥
0
−
1
,
o
t
h
e
r
w
i
s
e
\phi(z) = \begin{cases} 1, if \ z \ge 0 \\ \\ -1,otherwise \end{cases}
ϕ(z)=⎩
⎨
⎧1,if z≥0−1,otherwise
在机器学习文献中,通常把负的阈值和权重
w
0
=
−
θ
w_0 = - \theta
w0=−θ 称为偏置。
原理
感知器模型背后的逻辑是用还原论的方法来模拟大脑神经元的工作情况:要么触发要么不触发。
还原论:也称为还原主义,是一种哲学思想,认为复杂的系统、事物、现象可以将其化解为各部分之组合来加以理解和描述。
感知器的规则总结为以下几步:
- 把权重初始化为0或小的随机数
- 对每个训练样本 x ( i ) : x^{(i)} : x(i):
- 计算输出值 y ^ \hat{y} y^
- 更新权重 (回到第 2 步 ⇑ \Uparrow ⇑)
输出值为单位阶跃函数预测的预先定义好的类标签,比如: x ( i ) x^{(i)} x(i) 输入运算后,输出 y ( i ) y^{(i)} y(i)。
根据上述感知器规则,接下来需要更新权重,在感知器中,权重的更新表达式为:
w
j
=
w
j
+
△
w
j
w_j = w_j + \vartriangle w_j
wj=wj+△wj
△
w
j
\vartriangle w_j
△wj 用来更新的
w
j
w_j
wj 值,
△
w
j
\vartriangle w_j
△wj 根据感知器的学习规则计算:
△
w
j
=
η
(
y
(
i
)
−
y
^
(
i
)
)
x
j
(
i
)
\vartriangle w_j = \eta (y^{(i)} - \hat{y}^{(i)})x_j^{(i)}
△wj=η(y(i)−y^(i))xj(i)
η \eta η 是预先规定好的学习率, y ( i ) y^{(i)} y(i)是该项样本正确分类的类别, y ^ ( i ) \hat{y}^{(i)} y^(i)是感知器预测出的类别,在此需要注意,感知器模型中权重是和特征的维数相匹配的有多少个 x j x_j xj 就有多少个 w j w_j wj ,而所有的 w j w_j wj 是同时更新的,因此在所有 w j w_j wj 更新完之前,不会重新计算 y ^ ( i ) \hat{y}^{(i)} y^(i)。
这里说一下 这个学习规则的原理:
- η \eta η 是手动设置的学习率,控制后面更新的幅度的,这个很好理解
- ( y ( i ) − y ^ ( i ) ) (y^{(i)} - \hat{y}^{(i)}) (y(i)−y^(i)) 是判别正误的部分是必要的参数,如果没有错误,显然 w j w_j wj 不需要根据这部分数据更新
- 最后就是这个比较费解的 x j ( i ) x_j^{(i)} xj(i) 这里给出一种 最小二乘法 的理解:
- 上一篇文章 中给出过最小二乘的偏导推理其中 k k k(和 w w w 的位置相似) 的偏导结果是
2 ( y i − k x i − b ) x i 2(y_i - kx_i - b)x_i 2(yi−kxi−b)xi- 这里的调整目标和这个相似,因此也同理 × x j ( i ) \times x_j^{(i)} ×xj(i)
给出流程图 (参考 图片)
代码实现
原理懂了,动手干活。因为计算比较简单,所以没有用上 pytorch
等特高级的库, numpy
轻松搞定,pandas
和 matplotlib
做数据读写和可视化。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
#将感知器接口定义为一个类,允许初始化新的Perceptron对象,对象通过fit方法从数据中学习,通过predict方法进行预测,
#作为约定我们在对象初始化时未创建但通过对象的其他方法创建的属性的后面添加下划线(_),例如self.w_。
class Perceptron(object):
"""
参数详解:
w_:1维的数组,是模型训练后的权重
errors_:列表,每次迭代错误分类的数量
X:数组,形状=[n个样本,k个特征]
y:数组,形状=[n个样本]是模型计算的目标
"""
#对象初始化,初始化需要的学习率,迭代轮数,随机种子
def __init__(self, eta = 0.01, n_iter = 50, random_state = 1):
#学习率为0.01,迭代50次,随机种子为1
self.eta = eta
self.n_iter = n_iter
self.random_state = random_state
#训练模型
def fit(self, X, y):
rgen = np.random.RandomState(self.random_state)
#通过正态分布随机初始化模型权重
self.w_ = rgen.normal(loc=0.0, scale=0.01, size= 1 + X.shape[1])
#self.error_保存每次迭代中分类的错误,可以在后期分析训练阶段感知器的表现
self.errors_ = []
#开始迭代
for _ in range(self.n_iter):
#记录这次迭代预测错误的次数
errors = 0
#zip返回两个可迭代序列中元素元组组成的迭代器
# 如seq1 = [1, 2], seq_2 = [3, 4], 那么迭代zip(seq1, seq2)等价于迭代[(1, 3), (2, 4)]
for xi, target in zip(X, y):
update = self.eta * (target - self.predict(xi))
#self.w_[0]是添加上去的一个值
self.w_[1:] += update * xi
self.w_[0] += update
errors += int(update != 0.0)
#记录该次迭代错误分类有几个
self.errors_.append(errors)
return self
#计算向量点积
def net_input(self, X):
return np.dot(X, self.w_[1:]) + self.w_[0]
#预测
def predict(self, X):
return np.where(self.net_input(X) >= 0.0, 1, -1)
if __name__ == "__main__":
#读取数据
url = 'https://www.gairuo.com/file/data/dataset/iris.data'
df = pd.read_csv(url)
df.tail()
y = df.iloc[0:100, 4].values#0-99行,第四列的数据
# 因为 np.dot是向量点积所以X = df.df.iloc[0:100, [0,2]].values,不然会报错
y = np.where(y == 'setosa', -1, 1)#将setosa设置为-1,其余为1
X = df.iloc[0:100, [0,2]].values #取0-99行的第0列和第2列
#绘制散点图
#这里需要解释一下,为什么要将X分为X[:50,0]和X[50:100, 0],观察数据集可知,在鸢尾花数据集中,数据的排列是有序的,每一种花都是排列在一起的且数量为50,而setosa为前50个,versicolor为后50个。
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 length [cm]')
plt.ylabel('petal length [cm]')
#设置图例所在位置
plt.legend(loc='upper left')
plt.show()
#实例化模型
ppn = Perceptron(eta=0.1, n_iter=10)
#训练
ppn.fit(X, y)
##图中点的横坐标为1~len(pnn.errors_),y坐标为errors_,形状为'o'
plt.plot(range(1, len(ppn.errors_) + 1),
ppn.errors_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Number of updates')
plt.show()
最后配上一个运行图,收官,希望能帮助到大家。别忘了关注 收藏 点赞 哦