天天看点

基于PSO优化的图像识别网络

前言

代码思路参考了 https://blog.csdn.net/weixin_49352711/article/details/115559942?utm_source=app&app_version=4.5.8

做这个实验的目的是为了完成课程设计,加深对PSO的理解,网络最终的测试结果也证明,在图像识别网络中,添加粒子群寻优并不能提升训练速度和网络性能。

粒子群优化算法

粒子群优化算法(Particle Swarm Optimization,简称PSO),是1995年Eberhart博士和Kennedy博士一起提出的,它源于对鸟群捕食行为的研究。粒子群优化算法的基本核心是利用群体的个体对信息的共享从而使得整个群体的运动在问题求解空间中产生无序到有序的演化过程,从而获得问题的最优解。

粒子群算法的目标是使所有粒子在空间中找到最优值。初始化时,给空间中所有粒子分配初始随机位置和初始随机速度;根据每个粒子的速度、问题空间中已知的全局最优位置和粒子已知的最优位置依次迭代推进每个粒子的位置。随之计算的推移,通过探索和利用空间中已知的有利位置,粒子围绕一个或多个最优点聚集或聚合。在搜索过程中,保留全局最优位置和个体历史最优位置信息,有利于加快收敛速度,避免过早陷入局部最优解。

在搜索过程中粒子的速度更新公式由个体最优位置、全局最优位置和个体当前位置所决定,如式1所示。

基于PSO优化的图像识别网络

其中c1和c2被称为社会学习因子和个体学习因子,Xi为个体当前位置。rand()是0到1之间的随机数。计算出速度更新量后,根据公式2求出位置更新量。

基于PSO优化的图像识别网络

上述两式称为PSO的标准形式。其中公式1-1的第一部分称为记忆项,表示上次速度大小和方向的影响,第二部分称为自身认知项,是当前位置指向个体最优位置的矢量,表示粒子的动作来源于经验部分,第三部分称为群体认知项,是当前位置指向全局最优位置的矢量,反映了粒子间的协同合作和知识共享。

在标准形式的基础上引入非负的惯性因子w,其值较大,全局寻有能力强,局部寻优能力弱;其值较小,全局寻优能力弱,局部寻优能力强。如式3所示

基于PSO优化的图像识别网络

图像识别网络

采用MNIST数据集训练并测试实验所用的图像识别网络。网络结构依次为两个全连接层、BN层、Relu激活函数的组合以及最后的单独全连接层。第一层全连接层的输入维度为28*28,输出维度为300;第二层全连接层的输入维度为300,输出维度为100;最后的全连接层输入维度为100,输出维度为10,对应手写数字分类的10个类别。

利用PSO优化网络权重

利用粒子群优化神经网络权重的思路,是将网络中各神经元的权重用粒子位置来表示,网络的损失函数作为衡量粒子位置是否是最优的适应度函数。网络训练过程中,在梯度下降的基础上,引入粒子群优化算法,每一次粒子速度和粒子位置的更新,相当于神经网络经历了一个epoch的训练。利用PSO优化网络权重的流程如图所示。

基于PSO优化的图像识别网络

实验结果

在Pytorch的环境下对上述思路进行实现。网络超参数及PSO初始化参数的设置如表所示。考虑算法的可实施性,在实际实现中,仅对全连接层的权重使用粒子群优化与梯度下降法结合的方式进行优化迭代,对于BN层的权重使用梯度下降法进行更新。神经网络采用随机梯度下降法,损失函数选取交叉熵函数。

基于PSO优化的图像识别网络

经过30次迭代,每次迭代均使用MNIST数据集训练网络,返回损失函数值作为粒子群优化的适应度函数值,对粒子的速度、位置进行更新。训练过程持续1788S,神经网络的损失函数如图所示,粒子群优化参与到神经网络训练中,能够保证损失函数的整体下降趋势。

基于PSO优化的图像识别网络

使用测试集对网络效果进行测试。对网络预测成功的图片个数进行统计,计算该图像识别网络的正确率。经计算,正确率为:0.8144。

与常规训练过程对比

去掉粒子群优化,仅使用梯度下降对网络进行训练,设置epoch与max_iter相等,保持学习率、批次不变,训练时间为611.51S(CPU),训练过程中损失函数变化如图所示。

基于PSO优化的图像识别网络

经过粒子群优化的神经网络相较于传统训练的神经网络,性能较差,训练时间更长,这和粒子群优化过程中的随机性有关。同时,出于程序设置的原因,权重数目与粒子位置数目并不匹配,这也造成了计算冗余。尽管传统神经网络在准确率和训练时间上都优于基于PSO优化权重的图像识别网络,但本次实验加深了我对粒子群优化算法、神经网络的认识,是一次有意义的尝试。

代码

import os
import torch
from torch import nn, optim
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import FullyConnect3
import numpy as np
import pandas as pd
import random
import time


# 设置超参数
batch_size = 64
learning_rate = 1e-2
whether_tet = True
model_dir = "./PSOinFullyConnect3_First.pth"
loss_value = []



dara_tf = transforms.Compose(
    [transforms.ToTensor(),  # 范围为0-1
     transforms.Normalize([0.5], [0.5])])   # 单通道,均值0.5,方差0.5

# 下载训练集NMIST手写数字训练集
train_dataset = datasets.MNIST(
    root="./data", train=True, transform=dara_tf, download=False)
test_dataset = datasets.MNIST(
    root="./data", train=False, transform=dara_tf)

train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)


def tra_model(theWight):
    model.train()
    # print("现在进行权重的更新")
    model.layer1[0].weight.data = torch.from_numpy(theWight[0, :, :]).to(torch.float32)
    model.layer2[0].weight.data = torch.from_numpy(theWight[1, 0:100, 0:300]).to(torch.float32)
    model.layer3.weight.data = torch.from_numpy(theWight[2, 0:10, 0:100]).to(torch.float32)

    for c, data in enumerate(train_loader, 1):
        # print("当前训练批次为: {}次 ".format(c))
        img, label = data
        img = img.view(img.size(0), -1)
        img = Variable(img)
        label = Variable(label)

        out = model.forward(img)
        loss = criterion(out, label)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        loss_value.append(loss.data)
        # print("当前损失函数为:", loss)
    return loss


model = FullyConnect3.SimpleNet(in_ch=28*28, n_hidden_1=300, n_hidden_2=100, out_ch=10)

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=learning_rate)

# 粒子数量
num = 3

# 粒子位置矩阵维数 考虑三层全连接层权重的尺寸依次为[300, 784]\[100, 300]\[10, 100]
numx = 3
numy = 300
numz = 784

# p为粒子位置矩阵,初始为标准正态分布
p = np.random.randn(num, numx, numy, numz)

# v为粒子速度矩阵,初始为标准正态分别
v = np.random.randn(num, numx, numy, numz)

# 个体最佳位置
perbest_P = np.array(p, copy=True)

# 全局最佳位置
allbest_P = np.zeros((numx, numy, numz))

# 更新适应度函数
new_value = np.zeros(num)

# 粒子个体历史最优值
perbest_V = np.zeros(num)

# 粒子群体历史最优值
allbest_V = 0


# 计算初始粒子群的目标函数值(伴随梯度下降)
for i in range(num):
    perbest_V[i] = tra_model(p[i, :, :, :])


# 确定群体历史最优值
allbest_V = min(perbest_V)

# 确定初始最优位置
allbest_P = p[np.argmin(perbest_V), :, :, :]

# 设置最大迭代次数
max_iter = 30

# 设置速度更新权重值
w_forV = 0.1

# 开始迭代
stat_time = time.time()
for i in range(max_iter):
    print("当前迭代轮数: ", i)

    # 速度更新
    v = w_forV * v + 2.4 * random.random() * (allbest_P - p) + 1.7 * random.random() * (perbest_P - p)

    # 位置更新
    p = p + v

    # 计算每个粒子到达新位置后所得目标的函数值
    for a in range(num):
        new_value[a] = tra_model(p[a, :, :, :])

    # 更新全局最优
    if min(new_value) < allbest_V:
        allbest_V = min(new_value)
        allbest_P = p[np.argmin(perbest_V), :, :, :]

    # 更新个体历史最优
    for b in range(num):
        if new_value[b] < perbest_V[b]:
            perbest_V[b] = new_value[b]
            perbest_P[b, :, :, :] = p[b, :, :, :]
end_time = time.time() - stat_time
print(end_time)
state = {'model': model.state_dict(), 'optimizer': optimizer.state_dict()}
torch.save(state, model_dir)
saveLoss = pd.DataFrame(data=loss_value)
saveLoss.to_csv('./lossforMaxNum20.csv')
print("损失函数最小值为: ", allbest_V)
print("对应权重值为: ", allbest_P)
           

注:FullyConnect3为上述过程中自定义三层全连接网络。

继续阅读