机器学习
基本概念
分类(classification):预测结果是分散的。
回归(regression):预测结果是线性的。
样例:
实例:每个实例有一个特征向量
标签(label):
特征向量:一条记录属性的集合 (一维向量)
特征值:每个属性的值
属性:标签字段
训练集:训练样例用来进行训练,产生模型或算法的数据集。
测试集:用来专门进行测试,已经学习好的模型或算法的数据集。
![image-20220513192354571](https://cdn.jsdelivr.net/gh/Diacr/Notebook/20220513192354.png)
机器学习基本步骤
- 把数据拆分为训练集和数据集
- 用训练集和训练集的特征向量来训练算法
- 用学习来的算法运用在测试集上来评估算法(调整参数,让模型变得更加准确)
决策树ID3算法
ID3算法简介
熵
一条信息的信息量的大小和它的不确定性有直接的关系。 表示信源的不确定程度。
算法简介
根据信息增益来划分特征字段作为节点分类,然后递归地构建决策树.
计算信息增益
Gain(A) = info(D) - info_A(D) :是指在计算随机的变量D的信息熵,与加入变量A后的条件熵后。使用原来的信息熵减去条件熵后的得到的信息增益。信息增量值大,则更重要。取其作为根节点 。
info(D);信息熵的计算 。单位bits
H
(
x
)
=
−
∑
i
=
1
n
p
(
x
i
)
l
o
g
p
(
x
i
)
H(x)=-\sum_{i=1}^{n}{p_{(x_i)}}log^{p_{(x_i)}}
H(x)=−i=1∑np(xi)logp(xi)
info_A(D):是指在条件A发生的情况下,D的不确定性。(随机变量Y的条件下,随机变量 X 的不确定性。)(权 *信息熵 + 权 *信息熵)
H
(
Y
∣
X
)
=
∑
x
∈
X
p
(
x
)
H
(
Y
∣
X
=
x
)
H(Y|X)=\sum_{x\in X}{p_{(x)}}H{(Y|X=x)}
H(Y∣X)=x∈X∑p(x)H(Y∣X=x)
ID3算法实现
# -*- coding: UTF-8 -*-
from math import log
import operator
"""
函数说明:创建测试数据集
"""
def createDataSet():
dataSet = [[0, 0, 0, 0, 'no'], # 数据集
[0, 0, 0, 1, 'no'],
[0, 1, 0, 1, 'yes'],
[0, 1, 1, 0, 'yes'],
[0, 0, 0, 0, 'no'],
[1, 0, 0, 0, 'no'],
[1, 0, 0, 1, 'no'],
[1, 1, 1, 1, 'yes'],
[1, 0, 1, 2, 'yes'],
[1, 0, 1, 2, 'yes'],
[2, 0, 1, 2, 'yes'],
[2, 0, 1, 1, 'yes'],
[2, 1, 0, 1, 'yes'],
[2, 1, 0, 2, 'yes'],
[2, 0, 0, 0, 'no']]
labels = ['年龄', '有工作', '有自己的房子', '信贷情况'] # 分类属性
return dataSet, labels # 返回数据集和分类属性
"""
函数说明:计算给定数据集的经验熵(香农熵)
Parameters:
dataSet - 数据集
Returns:
shannonEnt - 经验熵(香农熵)
"""
def calcShannonEnt(dataSet):
numEntires = len(dataSet) # 返回数据集的行数
labelCounts = {} # 保存每个标签(Label)出现次数的字典
for featVec in dataSet: # 对每组特征向量进行统计
currentLabel = featVec[-1] # 提取标签(Label)信息
if currentLabel not in labelCounts.keys(): # 如果标签(Label)没有放入统计次数的字典,添加进去
labelCounts[currentLabel] = 0
labelCounts[currentLabel] += 1 # Label计数
shannonEnt = 0.0 # 经验熵(香农熵)
for key in labelCounts: # 计算香农熵
prob = float(labelCounts[key]) / numEntires # 选择该标签(Label)的概率
shannonEnt -= prob * log(prob, 2) # 利用公式计算
return shannonEnt # 返回经验熵(香农熵)
"""
函数说明:按照给定特征划分数据集
Parameters:
dataSet - 待划分的数据集
axis - 划分数据集的特征
value - 需要返回的特征的值
"""
def splitDataSet(dataSet, axis, value):
retDataSet = [] # 创建返回的数据集列表
for featVec in dataSet: # 遍历数据集
if featVec[axis] == value:
reducedFeatVec = featVec[:axis] # 去掉axis特征
reducedFeatVec.extend(featVec[axis + 1:]) # 将符合条件的添加到返回的数据集
retDataSet.append(reducedFeatVec)
return retDataSet # 返回划分后的数据集
"""
函数说明:选择最优特征
Parameters:
dataSet - 数据集
Returns:
bestFeature - 信息增益最大的(最优)特征的索引值
"""
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1 # 特征数量
baseEntropy = calcShannonEnt(dataSet) # 计算数据集的香农熵
bestInfoGain = 0.0 # 信息增益
bestFeature = -1 # 最优特征的索引值
for i in range(numFeatures): # 遍历所有特征
# 获取dataSet的第i个所有特征
featList = [example[i] for example in dataSet]
uniqueVals = set(featList) # 创建set集合{},元素不可重复
newEntropy = 0.0 # 经验条件熵
for value in uniqueVals: # 计算信息增益
subDataSet = splitDataSet(dataSet, i, value) # subDataSet划分后的子集
prob = len(subDataSet) / float(len(dataSet)) # 计算子集的概率
newEntropy += prob * calcShannonEnt(subDataSet) # 根据公式计算经验条件熵
infoGain = baseEntropy - newEntropy # 信息增益
print("第%d个特征的增益为%.3f" % (i, infoGain)) # 打印每个特征的信息增益
if (infoGain > bestInfoGain): # 计算信息增益
bestInfoGain = infoGain # 更新信息增益,找到最大的信息增益
bestFeature = i # 记录信息增益最大的特征的索引值
return bestFeature # 返回信息增益最大的特征的索引值
"""
函数说明:统计classList中出现此处最多的元素(类标签)
Parameters:
classList - 类标签列表
Returns:
sortedClassCount[0][0] - 出现此处最多的元素(类标签)
"""
def majorityCnt(classList):
classCount = {}
for vote in classList: # 统计classList中每个元素出现的次数
if vote not in classCount.keys():
classCount[vote] = 0
classCount[vote] += 1
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) # 根据字典的值降序排序
return sortedClassCount[0][0] # 返回classList中出现次数最多的元素
"""
函数说明:递归构建决策树
Parameters:
dataSet - 训练数据集
labels - 分类属性标签
featLabels - 存储选择的最优特征标签
Returns:
myTree - 决策树
"""
def createTree(dataSet, labels, featLabels):
classList = [example[-1] for example in dataSet] # 取分类标签(是否放贷:yes or no)
if classList.count(classList[0]) == len(classList): # 如果类别完全相同则停止继续划分
return classList[0]
if len(dataSet[0]) == 1: # 遍历完所有特征时返回出现次数最多的类标签
return majorityCnt(classList)
bestFeat = chooseBestFeatureToSplit(dataSet) # 选择最优特征
bestFeatLabel = labels[bestFeat] # 最优特征的标签
featLabels.append(bestFeatLabel)
myTree = {bestFeatLabel: {}} # 根据最优特征的标签生成树
del (labels[bestFeat]) # 删除已经使用特征标签
featValues = [example[bestFeat] for example in dataSet] # 得到训练集中所有最优特征的属性值
uniqueVals = set(featValues) # 去掉重复的属性值
for value in uniqueVals:
subLabels = labels[:]
# 递归调用函数createTree(),遍历特征,创建决策树。
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels, featLabels)
return myTree
"""
函数说明:使用决策树执行分类
Parameters:
inputTree - 已经生成的决策树
featLabels - 存储选择的最优特征标签
testVec - 测试数据列表,顺序对应最优特征标签
Returns:
classLabel - 分类结果
"""
def classify(inputTree, featLabels, testVec):
firstStr = next(iter(inputTree)) # 获取决策树结点
secondDict = inputTree[firstStr] # 下一个字典
featIndex = featLabels.index(firstStr)
for key in secondDict.keys():
if testVec[featIndex] == key:
if type(secondDict[key]).__name__ == 'dict':
classLabel = classify(secondDict[key], featLabels, testVec)
else:
classLabel = secondDict[key]
return classLabel
if __name__ == '__main__':
dataSet, labels = createDataSet()
featLabels = []
myTree = createTree(dataSet, labels, featLabels)
print(myTree)
testVec = [0, 1] # 测试数据
result = classify(myTree, featLabels, testVec)
if result == 'yes':
print('放贷')
if result == 'no':
print('不放贷')
KNN临近算法
KNN算法简介
-
KNN(K-NearesNeighbor) 即K邻近法,是一个理论上比较成熟的、也是最简单的机器学习算法之一。用老话就说:“人以群分,物以类聚”。
-
核心思想
一个样本与数据集中的k个样本最相似, 如果这k个样本中的大多数属于某一个类别, 则该样本也属于这个类别。也就是说,该方法在确定分类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。KNN方法在类别决策时,只与极少量的相邻样本有关。 -
欧氏距离(Euclidean Distance)
β = ( x 2 − x 1 ) 2 + ( y 2 − y 1 ) 2 β = \sqrt[]{(x_2-x_1)^2+(y_2-y_1)^2}\\ β=(x2−x1)2+(y2−y1)2
拓展到多维空间的公式:
d ( x , y ) = ( x 1 − y 1 ) 2 + ( x 2 − y 2 ) 2 + . . . + ( x n − y n ) 2 = ∑ i = 1 n ( x i − y i ) 2 d(x, y) = \sqrt[]{(x_1-y_1)^2+(x_2-y_2)^2+...+(x_n-y_n)^2}=\sqrt[]{\sum_{i=1}^n(x_i-y_i)^2} d(x,y)=(x1−y1)2+(x2−y2)2+...+(xn−yn)2=i=1∑n(xi−yi)2
KNN算法实现
numpy实现
import numpy as np
import operator
def knn(trainData, testData, labels, k):
tempData = np.tile(testData, (trainData.shape[0], 1)) #转化测试数据的数组结构,与训练数据一致
tempData = trainData - tempData #相减
tempData = tempData ** 2 #开平方
tempData = tempData.sum(axis=1) #求和
tempData = tempData ** 0.5 #开根号
sortData = np.argsort(tempData) #降序排序 得到的是排序后的下标顺序
count ={} #统计前K个类型数量
for i in range(k):
label = labels[i]
count[label] = count.get(label,0) + 1 #键首次出现赋默认值 再次出现则无效
sortData = sorted(count.items(),key=operator.itemgetter(1),reverse=True)
print(sortData[0][0])
#训练数据
trainData = np.array([
[115, 6],
[109, 8],
[120, 9],
[5, 78],
[6, 60],
[8, 69],
])
testData = np.array([100, 5]) #测试数据
labels = np.array(["动作片", "动作片", "动作片", "爱情片", "爱情片", "爱情片"]) #目标数据
print(knn(trainData, testData, labels, 3)) #调用函数
正常实现
import math
# 构建的数据集
def creatDataset():
dataset = [[3,104,'Romance'],
[2,100,'Romance'],
[1,81,'Romance'],
[101,10,'Action'],
[99,5,'Action'],
[98,2,'Action']]
return dataset
"""
Function:计算距离
Parameters: 测试数据坐标 x1,y1 训练数据坐标 x2,y2
Returns: 距离值
"""
def ComputeDistance(x1,y1,x2,y2):
d = math.sqrt((x1-x2)**2+(y1-y2)**2) #返回平方根
return d
"""
Function: 返回测试数据的目标值
Parameters: f 测试数据坐标[]
dataset 训练数据机
K 所取K值
Returns: 测试数据的目标值
"""
def ComputeAllDistance(f,dataset,k):
distances = [] #一个二维列表 存储每条数据的距离和目标值 [[距离,目标值],[]]
for data in dataset: #遍历每一条数据
film = [] #一维列表 存储 [距离,目标值]
d = ComputeDistance(f[0],f[1],data[0],data[1]) #调用函数计算距离值
film.append(d) #存储距离
film.append(data[-1]) #存储目标值
distances.append(film) #将单条数据的[距离,目标值]添加到二维列表中
distances = sorted(distances,key=(lambda x:x[0])) #将二维列表进行升序排序
print(distances)
result = {} #字典记录各目标值出现次数
for i in range(k):
type = distances[i][-1]
if type not in result.keys():
result[type] = 1 # result[type] = result.get(type,0) + 1
else:
result[type] += 1
r = sorted(result.items(),key=(lambda x:x[1]),reverse=True) #依据目标值出现次数进行降序排序
return r[0][0] #返回出现次数最多的目标值
d = ComputeAllDistance([18,90],creatDataset(),5)
print(d)
一元线性回归
回归模型的输出是连续型的数值。
线性回归简介
线性回归是用一条直线来拟合自变量和因变量之间的关系。 y = k ∗ x + b y = k*x+b y=k∗x+b
一个自变量一个因变量,两者之间的关系可以用一条直线近似表示,这种回归称为线性回归。
最小二乘法(误差平方和)
通过线性回归获得估计值
y
^
\hat{y}
y^,使用实际值y减去估计值
y
^
\hat{y}
y^后开平方,对所有样本求和。平方和越小则说明估计值与实际值越接近,反之亦然。
m
i
n
∑
1
n
(
y
−
y
^
)
2
min\sum_1^n(y-\hat{y})^2
min1∑n(y−y^)2
![image-20220509193035892](https://cdn.jsdelivr.net/gh/Diacr/Notebook/20220509193035.png)
损失函数.均方误差(Mean Square Error)
通过均方误差选择合适的k(斜率)b(截距)。
均方误差:反应真实值与预测值之间差异程度的一种度量,mse越小则说明预测的方程(直线)越合适。
1
m
∑
i
m
(
y
−
y
^
)
2
m
表
示
样
本
的
个
数
,
y
表
示
样
本
的
真
实
值
,
y
^
表
示
样
本
的
预
测
值
代
价
函
数
:
1
2
m
∑
i
m
(
y
−
y
^
)
2
y
^
=
k
∗
x
+
b
求
出
一
组
k
斜
率
,
b
截
距
使
得
代
价
函
数
取
得
最
小
值
,
可
以
使
用
梯
度
下
降
算
法
\frac{1}{m}\sum_i^m(y-\hat{y})^2\\ m表示样本的个数,y表示样本的真实值,\hat{y}表示样本的预测值\\ 代价函数:\frac{1}{2m}\sum_i^m(y-\hat{y})^2\\ \hat{y} = k*x+b\\ 求出一组k斜率,b截距使得代价函数取得最小值 ,可以使用梯度下降算法
m1i∑m(y−y^)2m表示样本的个数,y表示样本的真实值,y^表示样本的预测值代价函数:2m1i∑m(y−y^)2y^=k∗x+b求出一组k斜率,b截距使得代价函数取得最小值,可以使用梯度下降算法
最小化损失函数:回归任务是拟合样本点,使误差尽可能的小。
以
一
元
线
性
函
数
y
=
k
x
+
b
为
例
,
采
用
均
方
误
差
m
s
e
作
为
损
失
函
数
,
那
么
损
失
函
数
是
关
于
变
量
k
,
b
的
函
数
L
(
k
,
b
)
=
1
m
∑
i
m
(
(
k
x
i
+
b
)
−
y
i
)
2
m
,
为
样
本
个
数
。
此
时
任
务
为
最
小
化
L
(
k
,
b
)
那
么
如
何
获
得
L
(
k
,
b
)
的
最
小
值
呢
?
求
导
!
让
导
数
为
0
,
此
时
的
x
让
函
数
取
得
最
小
值
。
如
果
是
多
变
量
则
求
偏
导
,
让
偏
导
为
0.
以一元线性函数y=k x+b为例,采用均方误差mse作为损失函数,那么损失函数是关于变量k,b的函数\\ L(k, b) = \frac{1}{m}\sum_{i}^{m}((kx_{i}+b)-y_{i})^2\\ m,为样本个数。此时任务为最小化L(k, b)\\ 那么如何获得L(k, b)的最小值呢?\\ 求导!让导数为0,此时的x让函数取得最小值。如果是多变量则求偏导,让偏导为0.
以一元线性函数y=kx+b为例,采用均方误差mse作为损失函数,那么损失函数是关于变量k,b的函数L(k,b)=m1i∑m((kxi+b)−yi)2m,为样本个数。此时任务为最小化L(k,b)那么如何获得L(k,b)的最小值呢?求导!让导数为0,此时的x让函数取得最小值。如果是多变量则求偏导,让偏导为0.
代码实现(导数公式)
import numpy as np
#构建数据集
def createDataSet():
x = [1, 3, 2, 1, 3]
y = [14, 24, 18, 17, 27]
return x, y
#计算B1,计算B0
def compute(dataSet):
#求均值
average_x = np.mean(np.array(dataSet[0])) # sum(x) / len(x)
average_y = np.mean(np.array(dataSet[1]))
#定义变量
numerator = 0
denumerator = 0
#计算累加和
print(dataSet[0])
for index in range(len(dataSet[0])):
numerator += (dataSet[0][index] - average_x) * (dataSet[1][index] - average_y)
denumerator += (dataSet[0][index] - average_x) ** 2
#bi
b1 = numerator / denumerator
#b0
b0 = average_y - (b1 * average_x)
return b0, b1
#进行数据预测
def calculate(value, x):
calculateValue = value[0] + value[1]*x
return calculateValue
print(calculate(compute(createDataSet()), 10))
梯度下降(gradient_descent)
梯度下降是求解最大值或最小值最快的速度。
导数:一个函数在某一点处的导数,描述了这个函数在这一点附近的变化率。
导数的几何意义:函数在某点处的导数,是该点处的斜率。
一元函数:因变量的值,只取决于一个自变量的函数。(一个自变量,一个因变量)
多元函数:自变量不只是一个的函数。
关于某个自变量的变化率,就是多元函数对这个自变量求偏导数。计算关于某个变量的偏导数时,将其他变量看作常数。
◊ ◊ ◊ \Diamond\Diamond\Diamond ◊◊◊梯度下降:函数在某一点出沿着不同方向运动函数值的变化率是不同的,梯度可以定义为一个函数的全部偏导数构成的向量。梯度向量的方向是函数值变化率最大的方向。 对于函数的某个点,它的梯度就表示从该点出发函数值变化最为迅猛的方向。
为了求出函数 y ^ = k ∗ x + b \hat{y} = k*x+b y^=k∗x+b在取得最小值时 k 斜率,和b截距的值, 使用梯度下降算法。
对于函数的某个点,它的梯度就表示从改点出发函数值变化最为迅猛的方向。梯度向量的方向是函数值变化最大的方向。如果以负梯度的的方向来决定每次迭代时x(自变量)的变化方向,就会使得每次迭代后都可以令目标函数减小。当 F(x)导数 为0时此时自变量的值为最小值。
$$
x_i = x_{i-1} - a\dot{f}(x_{i-1})\
x_{i-1}表示上一个x(自变量取值)\
a 表示学习率\
\dot{f}(x_{i-1})表示上一点梯度\
- a\dot{f}(x_{i-1}) 即x_{i-1}沿负梯度向量移动的距离(一小步)\
x_i 新的x(自变量取值)\
如此反复迭代直到找到x(自变量取值)最小值
$$
import numpy as np
def getdata():
data = []# 保存样本集的列表
for i in range(100): # 循环采样100 个点
x = np.random.uniform(-10., 10.) # 随机采样输入x
# 采样高斯噪声
eps = np.random.normal(0, 0.01)
# 得到模型的输出
y = 1.477 * x + 0.089 + eps
data.append([x, y]) # 保存样本点
data = np.array(data) # 转换为2D Numpy 数组
return data
#梯度下降方式
def mse(b, w, points):
# 根据当前的w,b 参数计算均方差损失
totalError = 0
for i in range(0, len(points)): # 循环迭代所有点
x = points[i, 0] # 获得i 号点的输入x
y = points[i, 1] # 获得i 号点的输出y
# 计算差的平方,并累加
totalError += (y - (w * x + b)) ** 2
# 将累加的误差求平均,得到均方差
return totalError / float(len(points))
def step_gradient(b_current, w_current, points, lr):
# 计算误差函数在所有点上的导数,并更新w,b
b_gradient = 0
w_gradient = 0
M = float(len(points)) # 总样本数
for i in range(0, len(points)):
x = points[i, 0]
y = points[i, 1]
# 误差函数对b 的导数:grad_b = 2(wx+b-y),参考公式(2.3)
b_gradient += (2/M) * ((w_current * x + b_current) - y)
# 误差函数对w 的导数:grad_w = 2(wx+b-y)*x,参考公式(2.2)
w_gradient += (2/M) * x * ((w_current * x + b_current) - y)
# 根据梯度下降算法更新 w',b',其中lr 为学习率
new_b = b_current - (lr * b_gradient)
new_w = w_current - (lr * w_gradient)
return [new_b, new_w]
def gradient_descent(points, starting_b, starting_w, lr, num_iterations):
# 循环更新w,b 多次
b = starting_b # b 的初始值
w = starting_w # w 的初始值
# 根据梯度下降算法更新多次
for step in range(num_iterations):
# 计算梯度并更新一次
b, w = step_gradient(b, w, np.array(points), lr)
loss = mse(b, w, points) # 计算当前的均方差,用于监控训练进度
if step%50 == 0: # 打印误差和实时的w,b 值
print(f"iteration:{step}, loss:{loss}, w:{w}, b:{b}")
return [b, w] # 返回最后一次的w,b
def main():
# 加载训练集数据,这些数据是通过真实模型添加观测误差采样得到的
lr = 0.01 # 学习率
initial_b = 0 # 初始化b 为0
initial_w = 0 # 初始化w 为0
num_iterations = 1000
# 训练优化1000 次,返回最优w*,b*和训练Loss 的下降过程
data = getdata()
[b, w]= gradient_descent(data, initial_b, initial_w, lr, num_iterations)
loss = mse(b, w, data) # 计算最优数值解w,b 上的均方差
print(f'Final loss:{loss}, w:{w}, b:{b}')
main()
多元线性回归
最小二乘法(误差平方和)
通过线性回归获得估计值
y
^
\hat{y}
y^,使用实际值y减去估计值
y
^
\hat{y}
y^后开平方,对所有样本求和。平方和越小则说明估计值与实际值越接近,反之亦然。
m
i
n
∑
1
n
(
y
−
y
^
)
2
min\sum_1^n(y-\hat{y})^2
min1∑n(y−y^)2
![image-20220509193035892](https://cdn.jsdelivr.net/gh/Diacr/Notebook/20220509193035.png)
逻辑回归
逻辑回归简介
分类问题: 判断每条数据所属类别。
二分类问题 : 当分类问题的目标列只有两种结果时,就是二分类问题。
逻辑回归解决二分类问题。分类模型的输出是离散的。
逻
辑
回
归
=
线
性
回
归
+
s
i
g
m
o
i
d
函
数
线
性
回
归
z
=
k
∗
x
+
b
s
i
g
m
o
i
d
函
数
y
=
1
1
+
e
−
z
逻
辑
回
归
y
=
1
1
+
e
−
(
k
∗
x
+
b
)
逻辑回归 = 线性回归 + sigmoid函数\\ 线性回归\quad z = k*x + b\\ sigmoid函数\quad y = \frac{1}{1+e^{-z}}\\ 逻辑回归\quad y = \frac{1}{1+e^{-(k*x+b)}}
逻辑回归=线性回归+sigmoid函数线性回归z=k∗x+bsigmoid函数y=1+e−z1逻辑回归y=1+e−(k∗x+b)1
逻辑回归损失函数 ,体现 “预测值” 与 “实际值” 相似程度的函数。损失函数越小则模型越好。
C
=
−
[
y
l
n
a
+
(
1
−
y
)
l
n
(
1
−
a
)
]
C = -[y ln^a+(1 - y) ln^{(1 - a)}]
C=−[ylna+(1−y)ln(1−a)]
BP神经网咯
简介
pass
前向计算
sigmoid[(((值x权重)累加求和 )+ 偏向)]
逆向计算
输出层误差
当前神经元计算值(1-当前神经元计算值)x(输出的标签值-当前神经元计算值 )
隐藏层误差
当前神经元计算值(1-当前神经元计算值)x {(下一层误差*到下一层权重)累加求和}
权重更新
增量 = 学习率(0-1)*指向神经元误差 x 起始段神经元值
更新后权重 = 原权重 + 增量
偏向更新
增量 = 学习率当前神经元误差
更新后偏向 = 原偏向 + 增量
代码实现
import numpy as np
import math
#过滤器
def logital(x):
return 1/(1+math.e**(-x))
# layer[10, 2, 1]
def getweight(layer):
#存储权重,和偏置
weights=[]
#循环两次 range(2)
for i in range(len(layer)-1):
#i != 1
if i!=len(layer)-2:
#生成输入层和隐藏层之间的权重 生成权重矩阵
#生成11行3列的矩阵,值属于 -0.5 ---- 0.5 之间,前10行表示输入层到隐藏层的权重,第11行表示隐藏层的偏向。
#将输入层和隐藏层之间的权重矩阵添加到列表中
weights.append(np.random.random((layer[i]+1,layer[i+1]+1))-0.5)
else:
#生成隐藏层和输出层的权重矩阵 3行1列的矩阵 前两行表示权重,第3行表示输出层的偏向
#将隐藏层和输出层的权重矩阵添加到列表中
weights.append(np.random.random((layer[i]+1,layer[i+1]))-0.5)
return weights
#x 训练集中的特征向量 y 训练值中对应的标签
# weights 之前构建的随机权重列表
# learning_rate 学习率
# epoches 对14条数据循环1000次 1个epoches 表示所有的训练数据走完一遍
def fit(X,y,weights,learning_rate,epochs=1000):
#将x 转换为ndarray对象
X=np.array(X)
#向矩阵中第11 列插入 14 个 1,,axis=1代表列 axsi=0代表行
#x变成14行11列 第11列全是1
X=np.insert(X,X.shape[1],[1]*X.shape[0],axis=1)
for i in range(epochs):
#生成(0,13) 0--12之间的一个数
n=np.random.randint(0,X.shape[0]-1)
#取到第 n 行数据 转换成一个 1行 11列的一个一维向量
input = np.atleast_2d(X[n])
#输入层的神经元
a=[input]
# 前向计算
for i in range(len(weights)):
#使用客户信息矩阵1行11列 与 输入层与隐藏层之间的权重矩阵11行3列 进行点积
#得到隐藏层的值 1行3列 带入到激活函数logital中
#第二次循环使用隐藏层的值1行3列的值 与 隐藏层与输出层的权重矩阵 3行1列 进行点积 得到输处层的值 1行1列
a.append(logital(a[-1].dot(weights[i])))
#a 最后的值为 [[输入层的向量信息隐],[藏层的值],[输出层的值]]
#逆向计算 保存每一层误差
delta=[]
#输出层计算误差 输出层值(1-输出层的值)(输出的标签值-输出层的值 )
# y[n] 第行数据的目标值
error = a[-1]*(1-a[-1])*(y[n]-a[-1])
#将输出层误差添加到 delta 中
delta.append(error)
#计算隐藏层的误差
#此处只有一个隐藏层(可能有多个隐藏层)
#weights存放权重矩阵,长度减一即是隐藏层的个数
for i in range(len(weights)-1):
#隐藏层的误差 当前神经元计算值(1-当前神经元计算值){(下一层误差*到下一层权重)累加求和}
#a[-2] 隐藏层的值 delta[-1] 输出层的误差 weights[-1]隐藏层到输出层的权重
#输出层误差是1行1列 隐藏层与输出层权重矩阵是3行1列 无法进行点积,需要将权重矩阵转为1行3列
#最后得到1行3列的误差矩阵
hidden = a[-2-i]*(1-a[-2-i])*delta[-1].dot(weights[-1-i].T)
#将隐藏等的误差矩阵添加到delta中 此时delta为 [[输出层误差], [隐藏层误差]]
delta.append(hidden)
#reverse()将列表元素颠倒 此时delta为[[隐藏层误差],[输出层误差]]
delta.reverse()
#更新权重
for i in range(len(weights)):
#更新后权重 = 原权重 + (学习率(0-1)*下一层误差* 上一层的值)
#weights权重矩阵 a[0]输入层的值矩阵1行11列,转置后11行1列 , delta[0]表示隐藏层的误差矩阵
# a[1]隐藏层的值矩阵3行1列,转置后1行3列 , delta[1]表示输出层的误差矩阵
weights[i]+=learning_rate*a[i].T.dot(delta[i])
return weights
def predit(x,weights):
x.append(1)
input = np.atleast_2d(x)
a = [input]
for i in range(len(weights)):
a.append(logital(a[-1].dot(weights[i])))
return a[-1]
#将二维数组转换为ndarray对象
X = np.array([[1, 0, 0, 0, 0, 1, 1, 0, 1, 0], [1, 0, 0, 0, 0, 1, 1, 0, 0, 1],
[0, 1, 0, 0, 0, 1, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0],
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0], [0, 0, 1, 1, 0, 0, 0, 1, 0, 1],
[0, 1, 0, 1, 0, 0, 0, 1, 0, 1], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0],
[1, 0, 0, 1, 0, 0, 0, 1, 1, 0], [0, 0, 1, 0, 1, 0, 0, 1, 1, 0],
[1, 0, 0, 0, 1, 0, 0, 1, 0, 1], [0, 1, 0, 0, 1, 0, 1, 0, 0, 1],
[0, 1, 0, 0, 0, 1, 0, 1, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 0, 1]
])
#表示一个向量
y = np.array([0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0])
#weights = fit([[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]],[0,0,0,0,1,1,1,1],weights,0.9)
weights=getweight([X.shape[1],2,1]) #[10, 2, 1]
weights = fit(X,y,weights,0.9)
r = predit([0, 1, 0, 0, 0, 1, 0, 1, 1, 0],weights)
print(r)