神经网络neural network structure

分类

多层感知神经网络——最基础

卷积神经网络——善于图像识别

长短期记忆网络——善于语音识别

多层感知——数字识别

以一张28*28像素的单个数字图片为例,输出对应0-9

images/神经网络neural network structure-20240605105837739.webp

每个像素点的灰度值0-1,即输入为为28*28的矩阵

images/神经网络neural network structure-20240605105855005.webp

输入28*28=784个“神经元”neurons,每个神经元中装有代表一个像素灰度值的数字—该值“激活值”activation,构成第一层--输入层

输出0-9共10个神经元,其“激活值”0-1,代表概率,构成最后一层--输出层

输入层和输出层的中间层,称之为“隐藏层”hidden layers

隐藏层设置2层,每层16个神经元,隐藏层按顺序排列,每层输入来自上一层,输出给下一层,即上一层的激活值决定下一层的激活值

对于单个数字,其形状最多由5种类型笔画组成,(圈0,竖线|,横线—,半圈)

images/神经网络neural network structure-20240605105913546.webp

理想情况下,希望输出层的上一层能够对应数字的笔画部件,继而再往上一层推导,所以隐藏层就是识别目标的部分

每层神经元ai带上不同权重ωi,用于识别部分特征,加权和x=iωiai通过函数例如sigmoid=1/(1+ex)划归到0-1之间,对加权和处理的函数称为激活函数activation function,即获得下一层某个神经元的激活值
σ(x)=σ(iωiai),当然还得加上偏置值bias使得加权和不能随意激活,权重表示当前神经元关注的部分的像素图案,最终为表达:

σ(x)=σ(iωiai+b)

输入层到第二层为例,第一层的784个神经元与第二层的16个神经元两两连接,每根连接线上带有权重值,最终统计加权和+偏置值,并通过函数压缩到0-1,即这里的链接共有784*16个权重值ωi+16个偏置值bi.

简单计算,权重共有784*16+16*16+16*10;偏置16+16+10;需要设置13002个参数,即通过13002个开关来调节模型,以变成一个黑箱,神经网路训练就是找到合适的权重和偏置的一个过程,权重和偏置的实际含义还需要进一步挖掘

激活函数

激活函数主要分为饱和激活函数(Saturated Neurons)和非饱和函数(One-sided Saturations)。Sigmoid和Tanh是饱和激活函数,而ReLU以及其变种为非饱和激活函数。非饱和激活函数主要有如下优势:

  1. 非饱和激活函数可以解决梯度消失问题。
  2. 非饱和激活函数可以加速收敛。
  3. 单侧饱和还能使得神经元对于噪声干扰更具鲁棒性**。

饱和性

  1. 当我们的x趋近于正无穷,h(x)趋近于0,那么我们称之为右饱和。
  2. 当我们的x趋近于负无穷,h(x)趋近于0,那么我们称之为左饱和。
  3. 当一个函数既满足左饱和又满足右饱和的时候我们就称之为饱和,典型的函数有Sigmoid,Tanh函数。

稀疏性

是指大多数信道系数的能量较小,而几个能量较大的抽头分布相隔较远。

稀疏性(Sparsity),指的是模型具有非常大的容量,但只有模型的用于给定的任务、样本或标记的某些部分被激活。这样,能够显著增加模型容量和能力,而不必成比例增加计算量。

如果当神经元的输出接近于1的时候我们认为它被激活,而输出接近于0的时候认为它被抑制,那么使得神经元大部分的时间都是被抑制的限制则被称作稀疏性限制。

只要少部分中间隐藏神经元的活跃度,也就是输出值大于0,其他的大部分为0.原因就是我们要做的就是模拟我们人脑。

稀疏性参数,是一个比较接近于0的值,比如0.05.为了满足这个条件,我们得让大多数隐藏神经元的活跃度接近0.

符号标记

神经元ai(k), k表示神经网络第k层,输入层为0;

i表示当前层的第i个神经元 ,第一个为0
权重ωi,j,i表示链接映射到下一层的第i个神经元,与ai(k)中的i含义相同;j表示上一层的第j个神经元发出的链接

../all images/Pasted image 20240308173658.webp

偏置bi(k)表示第k1层第i1个神经元指向第k层第i个神经元的偏置

ai(k)=σ(jωi,j(k)aj(k1)+bi(k))

a0(1)=σ(jω0,jaj(0)+b0(1))

进而可以变成矩阵形式(a¯(k))k·1=σ[(Ω)k·n(a¯)n·1(k1)+(b¯(k))k·1],s是为列向量,到头来还是线性代数,pytorch中表示

a = sigmoid(np.dot(w,a) + b)

由此,神经元封装的是一个函数,输入为上一层所有神经元的输出,输出为0-1之间的数字;单纯用数字指代并不准确

整个神经元网络就是复合函数(该函数由13002个参数决定),将含有数字的图片像素灰度值矩阵映射到10*1的列向量,即784个值映射到10个值

sigmoid函数过时,ReLU(Rectified Linear Unit)训练效率更高

ReLU(x)={0,x<0x,x0

第一,采用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个变量的值,更新权重和偏置

../all images/Pasted image 20240308185106.webp

例如C(x,y)=3x2+y2, C(1,1)=[6,2],表明(1,1)沿着[3,1]方向走将使函数值C增长最快,也表明第一个变量x的重要性是第二个变量y的3倍,即在(1,1)邻域内,改变相同值,x对C的影响比y的更大,表示敏感度

  • 只能保证局部最小
  • 梯度下降法更新网络叫做反向传播
  • 采用梯度下降法,需要保证函数光滑,首先激活值是连续的

第四步,重复第二步。正个过程具有两个模型,神经网络模型和代价函数模型。
第五步,推理,手写数字识别准确。但是对于完全噪声的图片,模型也可能输出某个值,人工智能不一定智能。代价函数是不是评价模型的最优的手段呢?

核心算法——反向传播算法

第一步:对于最后一层输出层而言,通过梯度,记录输出层每个神经元对应上一层的神经元激活值需要变化的量,累加后即可得到倒数第二层激活值改动的变化值,对应权重和偏置

第二步:重复上一步骤,得到所有层的激活值,记录每个样本想怎样改变权重和偏置,最后取平均值

../all images/Pasted image 20240311101141.webp

实际,把训练样本打乱,分成很多组minibatch,每个minibatch包含一定数量的样本,得到局部最小值
最大的难度:获取标记好的训练数据

微积分原理

ai(k)=σ(jωi,jaj(k1)+bi(k))

以4*1的神经网络为例,Cost(ω1,b1,ω2,b2,ω3,b3,)=(a(L)y)2
又有a(L)=σ(ω(L)a(L1)+b(L)), 将其记作z(L)=ω(L)a(L1)+b(L), 所以a(L)=σ(z(L))

递推关系
第一个样本数据的函数代价函数C0,链式法则

C0ω(L)=C0a(L)a(L)z(L)z(L)ω(L)

已知a(L)y, C0a(L)=2(a(L)y);
激活函数σ得出a(L)z(L)=σ(z(L));
最后,z(L)ω(L)=a(L1)也是已知的

C0ω(L)=C0a(L)a(L)z(L)z(L)ω(L)=2(a(L)y)σ(z(L))a(L1)

所有n样本数据的代价函数平均值:

Cω(L)=1nk=0n1Ckω(L)

ω(L)替换成ω(i)b(i),i=1,2,,L,即C对每个权重和偏置进行偏导

C0b(L)=C0a(L)a(L)z(L)z(L)b(L)=2(a(L)y)σ(z(L))

C0a(L1)=C0a(L)a(L)z(L)z(L)a(L1)=2(a(L)y)σ(z(L))ω(L)

扩充到复杂网络

C0ωjk(L)=C0aj(L)aj(L)zj(L)zj(L)ωjk(L)

L1层激活值的偏导需要累加

C0ak(L1)=j=0nL1C0aj(L)aj(L)zj(L)zj(L)ak(L1)

学习率

../all images/Pasted image 20240311115828.webp

梯度下降法(Gradient Descent,GD)
使用少部分样本更新,随机梯度法(Stochastic Gradient Descent,SGD)
对于非凸函数,引入动量(moment)减少振荡,加入阻尼,动量随机梯度下降法,保持移动轨迹的平滑

../all images/Pasted image 20240311115713.webp

保持学习率,引入自适应算法AdaGrad(r变化只与梯度有关,学习率过早变小而不好控制)和RMSProp

../all images/Pasted image 20240311115844.webp

结合动量发和自适应法的Adam算法

../all images/Pasted image 20240311115203.webp

../all images/Pasted image 20240311115242.webp

矩阵乘法算法

1969年,strassen算法,7次乘法,O(n)=n2.81
1981年,layer算法,张量分析,O(n)=n2.522
目前,O(n)=n2.372860

代码实现

导入包

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)

作者:invo

出处:https://www.cnblogs.com/invo/p/18230542

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Invo1  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示