神经网络neural network structure
分类
多层感知神经网络——最基础
卷积神经网络——善于图像识别
长短期记忆网络——善于语音识别
多层感知——数字识别
以一张28*28像素的单个数字图片为例,输出对应0-9
每个像素点的灰度值0-1,即输入为为28*28的矩阵
输入28*28=784个“神经元”neurons,每个神经元中装有代表一个像素灰度值的数字—该值“激活值”activation,构成第一层--输入层
输出0-9共10个神经元,其“激活值”0-1,代表概率,构成最后一层--输出层
输入层和输出层的中间层,称之为“隐藏层”hidden layers
隐藏层设置2层,每层16个神经元,隐藏层按顺序排列,每层输入来自上一层,输出给下一层,即上一层的激活值决定下一层的激活值
对于单个数字,其形状最多由5种类型笔画组成,(圈0,竖线|,横线—,半圈)
理想情况下,希望输出层的上一层能够对应数字的笔画部件,继而再往上一层推导,所以隐藏层就是识别目标的部分
每层神经元
输入层到第二层为例,第一层的784个神经元与第二层的16个神经元两两连接,每根连接线上带有权重值,最终统计加权和+偏置值,并通过函数压缩到0-1,即这里的链接共有784*16个权重值
简单计算,权重共有784*16+16*16+16*10;偏置16+16+10;需要设置13002个参数,即通过13002个开关来调节模型,以变成一个黑箱,神经网路训练就是找到合适的权重和偏置的一个过程,权重和偏置的实际含义还需要进一步挖掘
激活函数
激活函数主要分为饱和激活函数(Saturated Neurons)和非饱和函数(One-sided Saturations)。Sigmoid和Tanh是饱和激活函数,而ReLU以及其变种为非饱和激活函数。非饱和激活函数主要有如下优势:
- 非饱和激活函数可以解决梯度消失问题。
- 非饱和激活函数可以加速收敛。
- 单侧饱和还能使得神经元对于噪声干扰更具鲁棒性**。
饱和性
- 当我们的
趋近于正无穷, 趋近于0,那么我们称之为右饱和。 - 当我们的
趋近于负无穷, 趋近于0,那么我们称之为左饱和。 - 当一个函数既满足左饱和又满足右饱和的时候我们就称之为饱和,典型的函数有Sigmoid,Tanh函数。
稀疏性
是指大多数信道系数的能量较小,而几个能量较大的抽头分布相隔较远。
稀疏性(Sparsity),指的是模型具有非常大的容量,但只有模型的用于给定的任务、样本或标记的某些部分被激活。这样,能够显著增加模型容量和能力,而不必成比例增加计算量。
如果当神经元的输出接近于1的时候我们认为它被激活,而输出接近于0的时候认为它被抑制,那么使得神经元大部分的时间都是被抑制的限制则被称作稀疏性限制。
只要少部分中间隐藏神经元的活跃度,也就是输出值大于0,其他的大部分为0.原因就是我们要做的就是模拟我们人脑。
稀疏性参数,是一个比较接近于0的值,比如0.05.为了满足这个条件,我们得让大多数隐藏神经元的活跃度接近0.
符号标记
神经元
权重
偏置
进而可以变成矩阵形式
a = sigmoid(np.dot(w,a) + b)
由此,神经元封装的是一个函数,输入为上一层所有神经元的输出,输出为0-1之间的数字;单纯用数字指代并不准确
整个神经元网络就是复合函数(该函数由13002个参数决定),将含有数字的图片像素灰度值矩阵映射到10*1的列向量,即784个值映射到10个值
sigmoid函数过时,ReLU(Rectified Linear Unit)训练效率更高
第一,采用sigmoid等函数,算激活函数时(指数运算),计算量大,反向传播求误差梯度时,求导涉及除法,计算量相对大,而采用Relu激活函数,整个过程的计算量节省很多。
第二,对于深层网络,sigmoid函数反向传播时,很容易就会出现 梯度消失 的情况(在sigmoid接近饱和区时,变换太缓慢,导数趋于0,这种情况会造成信息丢失),从而无法完成深层网络的训练。
第三,ReLu会使一部分神经元的输出为0,这样就造成了 网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题的发生。
当然现在也有一些对relu的改进,比如prelu,random relu等,在不同的数据集上会有一些训练速度上或者准确率上的改进。
训练
如何通过训练获得合适的权重和偏置?
MNIST数据库
第一步,将所有权重和偏置进行随机初始化
第二步,喂样本,模型吃一个样本数据后(吐)输出一个向量,通过损失函数来评价该输出向量与目标向量的差距,例如
数字3的图片输入,初始化模型输出【0.2,0.6,0.5,0.6,0.8,0.2,0.6,0.5,0.1,0.4】,与目标(期望)向量【0,0,0,1,0,0,0,0,0,0】进行取模,即平方差之和,称之为单个样本的代价cost,该和越接近0,表示判断越准确
训练上万个样本,代价的平均值,即可评价神经网络的准确性
训练过程就是由13002个参数决定的模型为输入,输出一个代价
第三步,修改权重和偏置,即全局极小值问题
先考虑局部最小值问题,步长与梯度值负相关,可以防止过头;沿着梯度负方向走,函数值下降的最快;
算法实现,13002个变量构成一个多元函数13002·1的列向量,对该函数先计算当前点的梯度13002·1的列向量,梯度值表明函数变量的影响步大小,长为梯度反方向,找到代价的最小值,对应获得13002个变量的值,更新权重和偏置
例如
- 只能保证局部最小
- 梯度下降法更新网络叫做反向传播
- 采用梯度下降法,需要保证函数光滑,首先激活值是连续的
第四步,重复第二步。正个过程具有两个模型,神经网络模型和代价函数模型。
第五步,推理,手写数字识别准确。但是对于完全噪声的图片,模型也可能输出某个值,人工智能不一定智能。代价函数是不是评价模型的最优的手段呢?
核心算法——反向传播算法
第一步:对于最后一层输出层而言,通过梯度,记录输出层每个神经元对应上一层的神经元激活值需要变化的量,累加后即可得到倒数第二层激活值改动的变化值,对应权重和偏置
第二步:重复上一步骤,得到所有层的激活值,记录每个样本想怎样改变权重和偏置,最后取平均值
实际,把训练样本打乱,分成很多组minibatch,每个minibatch包含一定数量的样本,得到局部最小值
最大的难度:获取标记好的训练数据
微积分原理
以4*1的神经网络为例,
又有
递推关系
第一个样本数据的函数代价函数
已知
激活函数
最后,
所有
将
扩充到复杂网络
对
学习率
梯度下降法(Gradient Descent,GD)
使用少部分样本更新,随机梯度法(Stochastic Gradient Descent,SGD)
对于非凸函数,引入动量(moment)减少振荡,加入阻尼,动量随机梯度下降法,保持移动轨迹的平滑
保持学习率,引入自适应算法AdaGrad(r变化只与梯度有关,学习率过早变小而不好控制)和RMSProp
结合动量发和自适应法的Adam算法
矩阵乘法算法
1969年,strassen算法,7次乘法,
1981年,layer算法,张量分析,
目前,
代码实现
导入包
import math
import csv
import numpy as np
读取数据
def loaddata(filename):
label = []
with open(filename, 'r') as f:
lines = csv.reader(f)
data = list(lines)
for i in range(len(data)):
del(data[i][0])
for j in range(len(data[i])):
data[i][j] = float(data[i][j])
label.append(data[i][-1])
del(data[i][-1])
return np.array(data), np.array(label)
sigmoid函数
def sigmoid(x):
return 1.0/(1 + np.exp(-x))
代价函数
def J(theta, X, Y, theLambda=0):
m, n = X.shape
h = sigmoid(np.dot(X, theta))
J = (-1.0/m)*(np.dot(np.log(h).T, Y)+np.dot(np.log(1-h).T, 1-Y)) + (theLambda/(2.0*m))*np.sum(np.square(theta[1:]))
return J.flatten()[0]
随机梯度下降
def gradient_sgd(X, Y, alpha=0.01, epsilon=0.00001, maxloop=1000, theLambda=0.0):
m, n = X.shape
theta = np.zeros((n, 1))
cost = J(theta, X, Y)
costs = [cost]
thetas = [theta]
# 随机梯度下降
count = 0
flag = False
while count < maxloop:
if flag:
break
for i in range(m):
h = sigmoid(np.dot(X[i].reshape((1, n)), theta))
theta = theta - alpha * (
(1.0 / m) * X[i].reshape((n, 1)) * (h - Y[i]) + (theLambda / m) * np.r_[[[0]], theta[1:]])
thetas.append(theta)
cost = J(theta, X, Y, theLambda)
costs.append(cost)
if abs(costs[-1] - costs[-2]) < epsilon:
flag = True
break
count += 1
if count % 100 == 0:
print("cost:", cost)
return thetas, costs, count
牛顿法
def gradient_newton(X, Y, epsilon=0.00001, maxloop=1000, theLambda=0.0):
m, n = X.shape
theta = np.zeros((n, 1))
cost = J(theta, X, Y)
costs = [cost]
thetas = [theta]
count = 0
while count < maxloop:
delta_J = 0.0
H = 0.0
for i in range(m):
h = sigmoid(np.dot(X[i].reshape((1, n)), theta))
delta_J += X[i] * (h - Y[i])
H += h.T * (1 - h) * X[i] * X[i].T
delta_J /= m
H /= m
print(H, delta_J)
theta = theta - 1.0 / H * delta_J
thetas.append(theta)
cost = J(theta, X, Y, theLambda)
costs.append(cost)
if abs(costs[-1] - costs[-2]) < epsilon:
break
count += 1
if count % 100 == 0:
print("cost:", cost)
return thetas, costs, count
Adagrad
def gradient_adagrad(X, Y, alpha=0.01, sigma=1e-7, epsilon=0.00001, maxloop=1000, theLambda=0.0):
m, n = X.shape
theta = np.zeros((n, 1))
r = [[0.0] for _ in range(n)]
cost = J(theta, X, Y)
costs = [cost]
thetas = [theta]
count = 0
flag = False
while count < maxloop:
if flag:
break
for i in range(m):
h = sigmoid(np.dot(X[i].reshape((1, n)), theta))
grad = (1.0 / m) * X[i].reshape((n, 1)) * (h - Y[i])
for j in range(n):
r[j].append(grad[j]**2+r[j][-1])
theta[j] = theta[j] - alpha * grad[j] / (sigma + math.sqrt(r[j][-1]))
thetas.append(theta)
cost = J(theta, X, Y, theLambda)
costs.append(cost)
if abs(costs[-1] - costs[-2]) < epsilon:
flag = True
break
count += 1
if count % 100 == 0:
print("cost:", cost)
return thetas, costs, count
Adadelta
def gradient_adadelta(X, Y, rho=0.01, alpha=0.01, sigma=1e-7, epsilon=0.00001, maxloop=1000, theLambda=0.0):
m, n = X.shape
theta = np.zeros((n, 1))
r = [[0.0] for _ in range(n)]
deltax = [[0.0] for _ in range(n)]
deltax_ = [[1.0] for _ in range(n)]
cost = J(theta, X, Y)
costs = [cost]
thetas = [theta]
count = 0
flag = False
while count < maxloop:
if flag:
break
for i in range(m):
h = sigmoid(np.dot(X[i].reshape((1, n)), theta))
grad = (1.0 / m) * X[i].reshape((n, 1)) * (h - Y[i])
for j in range(n):
r[j].append((1-rho) * grad[j]**2 + rho * r[j][-1])
deltax[j].append(- (math.sqrt(deltax_[j][-1] / sigma + r[j][-1]))*alpha)
theta[j] = theta[j] + deltax[j][-1]
deltax_[j].append((1-rho)*deltax[j][-1]**2+rho*deltax_[j][-1])
# print(deltax)
# print(deltax_)
thetas.append(theta)
cost = J(theta, X, Y, theLambda)
costs.append(cost)
if abs(costs[-1] - costs[-2]) < epsilon:
flag = True
break
count += 1
if count % 100 == 0:
print("cost:", cost)
return thetas, costs, count
RMSProp
def gradient_RMSProp(X, Y, rho=0.01, alpha=0.01, sigma=1e-7, epsilon=0.00001, maxloop=1000, theLambda=0.0):
m, n = X.shape
theta = np.zeros((n, 1))
r = [[0.0] for _ in range(n)]
cost = J(theta, X, Y)
costs = [cost]
thetas = [theta]
count = 0
flag = False
while count < maxloop:
if flag:
break
for i in range(m):
h = sigmoid(np.dot(X[i].reshape((1, n)), theta))
grad = (1.0 / m) * X[i].reshape((n, 1)) * (h - Y[i])
for j in range(n):
r[j].append((1 - rho)*grad[j]**2+rho*r[j][-1])
theta[j] = theta[j] - alpha * grad[j] / (sigma + math.sqrt(r[j][-1]))
thetas.append(theta)
cost = J(theta, X, Y, theLambda)
costs.append(cost)
if abs(costs[-1] - costs[-2]) < epsilon:
print(costs)
flag = True
break
count += 1
if count % 100 == 0:
print("cost:", cost)
return thetas, costs, count
Adam
def gradient_adam(X, Y, rho1=0.9, rho2=0.999, alpha=0.01, sigma=1e-7, epsilon=0.00001, maxloop=1000, theLambda=0.0):
m, n = X.shape
theta = np.zeros((n, 1))
s = [[0.0] for _ in range(n)]
r = [[0.0] for _ in range(n)]
cost = J(theta, X, Y)
costs = [cost]
thetas = [theta]
count = 0
flag = False
while count < maxloop:
if flag:
break
for i in range(m):
h = sigmoid(np.dot(X[i].reshape((1, n)), theta))
grad = (1.0 / m) * X[i].reshape((n, 1)) * (h - Y[i])
for j in range(n):
r[j].append((1 - rho2)*grad[j]**2+rho2*r[j][-1])
s[j].append((1 - rho1)*grad[j]+rho1*r[j][-1])
theta[j] = theta[j] - alpha * (s[j][-1]/(1-rho1**2))/(math.sqrt(r[j][-1]/(1-rho2**2))+sigma)
thetas.append(theta)
cost = J(theta, X, Y, theLambda)
costs.append(cost)
if abs(costs[-1] - costs[-2]) < epsilon:
print(costs)
flag = True
break
count += 1
if count % 100 == 0:
print("cost:", cost)
return thetas, costs, count
主程序
if __name__ == '__main__':
train_data, train_label = loaddata('pima_train.csv')
test_data, test_label = loaddata('pima_test.csv')
# print(train_data)
# print(train_label)
m = train_data.shape[0]
# print(m)
X = np.concatenate((np.ones((m, 1)), train_data), axis=1)
# print(X)
# thetas, costs, iterationCount = gradient_sgd(X, train_label, 0.05, 0.00000001, 10000)
# thetas, costs, iterationCount = gradient_newton(X, train_label, 0.00000001, 10000)
# thetas, costs, iterationCount = gradient_adagrad(X, train_label, 0.05, 1e-7, 0.000000000000001, 10000)
# thetas, costs, iterationCount = gradient_adadelta(X, train_label, 0.9999999999, 1e-5, 1e-7, 0.000000000000001, 10000)
# thetas, costs, iterationCount = gradient_RMSProp(X, train_label, 0.9, 1e-5, 1e-7, 0.000000000000001, 10000)
# thetas, costs, iterationCount = gradient_adam(X, train_label, 0.9, 0.99, 0.001, 1e-7, 0.000000000000001, 10000)
print(costs[-1], iterationCount)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人