神经网络 c++ 源码 可以直接复制运行,提供数据集,操作简单,最少仅需4行代码
本文的神经网络,让你省去Python那些花里胡哨的变量名,最少仅需4行代码即可完成自己的神经网络**
本文章采用c++ 编写神经网络,隐藏层提供了sigmoid 和 Relu方法,输出层采用了Softmax方法。
采用了数据集 ,http://archive.ics.uci.edu/ml/datasets/Letter+Recognition
验证集和训练集我都给你们分好了。
训练集
链接: https://pan.baidu.com/s/1BbbHuHtHhjgqBFhypzkuFQ 提取码: bgqq
测试集
链接: https://pan.baidu.com/s/1EdmMdNn_mYfxBlFn9_4rrQ 提取码: c3gc
本文就不太具体介绍神经网络如何运行了,因为网上全都是详细简介,但还是会基于少许的神经网络知识。
不好意思之前忘记,将权重优化加进去了,我使用了momentum
简介介绍
神经网络运作
如果你看过神经网络的图片,你会看见有几个点,然后用线连接着就可以运作了
点和线是什么?
.
我们根据下图讲解
点
我们可以从图片看到,点被分成了3种不同颜色的类型,每种颜色代表一个层。
红:代表输入层
绿:代表隐藏层
紫:代表输出层
输入层是什么?
比如: 0,0 代表A,0,1 代表B 1,0代表 C 1,1D ,我们拿A举例;那么输入层就应该输入x = 0 ,y = 0。
隐藏层是什么?
将你输入的一系列值采用非线性的方法在隐藏层中计算,隐藏层中的值都需要使用一次激活函数。激活函数的值将去向下一个隐藏层
输出层是什么
最后一层的隐藏层的激活函数得到的值将会去向输出层,然后输出层也使用一个激活函数,最终得到4个不同的概率(因为我采用的softmax作为输出层的激活函数)。
前向传播
每一层的值如何去向下一层
这里就需要说一下线是什么了,因为每一层的点都会被上一层的点用线连接着。
为什么要用线连接着
比如我们去一个超市,我们是不是要通过一条路才能去呢。那么每一个值去向下一个地方也需要一个通道。那就是图中每个点互相连接的线,只有互相连线了值才可以互通。
但是每个点去向下一个与他互通的点的时候,他们的道路都需要一个值。比如你去超市的路的长度. 在神经网络中我们称作权重,后面我也继续用这个词语描述。
如何计算点的值
除了输入层的点不需要计算的点的值,因为它的值是你输入进去的。
在神经网络中点的值计算是通过每个可以去向它的点的值乘权重,然后累加到一起与偏移值,再使用使用激活函数。
X:是点的值
W:去向下一个点的权重
b:偏移值 (除了输入层,每一层都有一个偏移值连向所有点,偏移值永远是1,只有偏移值的权重会变)
什么是激活函数
激活函数就是让点的值变成非线性
为什么要使用激活函数
因为不使用激活函数所有的值都是线性,线性就一条直线,那最终得到的值将毫无意义,直线要么上升要么就是下降。所以实际上点的值应该是最终使用激活函数得到的值。
代码中隐藏层使用了Relu或sigmoid激活函数,输出层使用了softmax激活函数.
~~到此就完成了前向传播的所有事情。
后向传播
向后传播就是优化你的权重,为什么要向后传播,因为你不可能第一次输入值神经网络就可以正确的将它分类把。神经网络也是一个需要不断学习的东西,它不断改进的方法就是在后向传播的时候实现。
神经网络如何不断改进自己的错误
通过判断最终得到的值,是不是和真实值相等,要是不相等就,搭配激活函数的导数和损失函数,反向传播回去。反向传播的主要目的就是更新权重,使得下一次正向传播的时候可以获得更好的结果。
~~这里就不过多做反向传播的讲解了,反正都是一堆导数,要是你没学过高等数学或者忘记了或者随便混过的就同等于看天书,然后还浪费我时间在这里讲,直接看代码,反正其他人也介绍了每个激活函数的导数如何搭配损失函数用。反正代码中也有导数的代码,直接上源码。
提示
作者测试过代码无论多少层,每层不同的节点都是可以完美运行的。
如果想要加不同的层,或者改动层的节点个数,请看清楚加层的方法名在使用。因为输入层和输出层连接隐藏层的方法是不同的。
QT
作者弄了个QT界面的效果,你们可以看到训练的效果,下面代码不含qt代码纯c++,qt界面就不给了,做的有点垃圾,你们喜欢可以自己搭建一个。
16000训练集,4000测试集, 对了3175个,只迭代了10次。3层128,64,32;
作者测试最大准确率可以达到百分之95%
测试单条数据,你们可以看到分类效果,最终结果吻合是U
globalVariable.hpp
#ifndef GLOBALVARIABLES_H
#define GLOBALVARIABLES_H
#include <vector>
#include <map>
#include <iostream>
#include <cmath>
#include <algorithm>
#define sqr(x) ((x) * (x))
using namespace std;
struct hiddenNode;
struct outputNode;
extern double n;
extern double alpha;
extern int HIDALOG;
extern int SIGMOID;
extern int RELU;
extern int TANH;
struct biasNode
{
double activation_vals;
vector<hiddenNode*>hidConnection;
vector<outputNode*>outConeection;
};
struct inputNode
{
int ID;
double curr_val;
vector<double> activation_vals;
vector<hiddenNode*> hidConnection;
vector<double> linkToHidden;
vector<double> vLinkToHO;
};
struct hiddenNode
{
int ID;
double activation_vals;
double z;
double error;
vector<outputNode*> outConnection;
vector<inputNode*> intConnection;
vector<hiddenNode*> hidUpConnection;
vector<hiddenNode*> hidDownConnectino;
biasNode* biasConnectino;
vector<double> linkToHO;
vector<double> vLinkToHO;
double linkTobias;
double vLinkTobias;
};
struct outputNode
{
int ID;
vector<double> actual_val;
double z;
double activation_vals;
double error;
vector<hiddenNode*> hidConnection;
biasNode* biasConnectino;
double linkTobias;
double vLinkTobias;
};
#endif /* Header_h */
Nerual.hpp
#ifndef BACKPROPAGATION_H
#define BACKPROPAGATION_H
#include "globalVariable.hpp"
class Nerual
{
public:
void setInputLayer(int num,vector<vector<double>> vals);
void setInputToHidden(int num);
void setHiddenToHidden(int num);
void setOutToHidden(int num,vector<vector<double>> vals);
public:
double sigmod(double sum);
double Relu(double sum);
double Softmax(double sum,double f);
double SoftmaxDerivative(double i,double j);
double sigmodDerivative(double act);
bool ReluDerivative(double act);
double weightUpdate(double act,double second_act);
double RAND_WEIGHT();
public:
void feedForward(int i);
void backPropagate(int i);
double Trainning_Network();
void testOneLine(double x[]);
void testSet(vector<vector<double>> vals,vector<string> tital);
private:
vector<inputNode*> input;
vector<vector<hiddenNode*>> hiddens;
vector<biasNode*> bias;
vector<outputNode*> output;
double tmp_sum;
double max;
};
#endif /* Nerual_hpp */
Nerual.cpp
//
// Nerual.cpp
// infrastructure
//
// Created by User on 2020/9/17.
// Copyright © 2020 User. All rights reserved.
//
#include "Nerual.hpp"
void Nerual::setInputLayer(int num,vector<vector<double>> vals)
{
for (int i=0; i<num; i++)
{
inputNode *tmp = new inputNode;
tmp->ID=i;
tmp->activation_vals = vals[i];
input.push_back(tmp);
}
}
double Nerual::RAND_WEIGHT()
{
return ( (static_cast<double>(rand()) / static_cast<double>(RAND_MAX) - 0.5) );
}
void Nerual::setInputToHidden(int num)
{
vector<hiddenNode*> hidden;
biasNode *bia = new biasNode;
hiddenNode *tmp = new hiddenNode[num];
bia->activation_vals=1;
for (int i=0; i<input.size(); i++)
{
for (int j=0; j<num; j++)
{
input[i]->linkToHidden.push_back(RAND_WEIGHT());
input[i]->vLinkToHO.push_back(0);
input[i]->hidConnection.push_back(&tmp[j]);
tmp[j].intConnection.push_back(input[i]);
}
}
for (int i=0; i<num; i++)
{
tmp[i].ID = i;
tmp[i].biasConnectino=bia;
tmp[i].linkTobias=RAND_WEIGHT();
tmp[i].vLinkTobias=0.0;
bia->hidConnection.push_back(&tmp[i]);
hidden.push_back(&tmp[i]);
}
bias.push_back(bia);
hiddens.push_back(hidden);
}
void Nerual::setHiddenToHidden(int num)
{
vector<hiddenNode*> lastLayer = hiddens[hiddens.size()-1];
vector<hiddenNode*> hidden;
hiddenNode *tmp = new hiddenNode[num];
biasNode *bia = new biasNode;
bia->activation_vals=1;
for (int i=0; i<lastLayer.size(); i++)
{
for (int j=0; j<num; j++)
{
lastLayer[i]->linkToHO.push_back(RAND_WEIGHT());
lastLayer[i]->vLinkToHO.push_back(0);
lastLayer[i]->hidUpConnection.push_back(&tmp[j]);
tmp[j].hidDownConnectino.push_back(lastLayer[i]);
}
}
for (int i=0; i<num; i++)
{
tmp[i].ID = i;
tmp[i].biasConnectino=bia;
tmp[i].linkTobias=RAND_WEIGHT();
tmp[i].vLinkTobias=0.0;
bia->hidConnection.push_back(&tmp[i]);
hidden.push_back(&tmp[i]);
}
bias.push_back(bia);
hiddens.push_back(hidden);
}
void Nerual::setOutToHidden(int num,vector<vector<double>> vals)
{
vector<hiddenNode*> lastLayer = hiddens[hiddens.size()-1];
biasNode *bia = new biasNode;
outputNode *tmp = new outputNode[num];
bia->activation_vals=1;
for (int i=0; i<lastLayer.size(); i++)
{
for (int j=0; j<num; j++)
{
lastLayer[i]->linkToHO.push_back(RAND_WEIGHT());
lastLayer[i]->vLinkToHO.push_back(0);
lastLayer[i]->outConnection.push_back(&tmp[j]);
tmp[j].hidConnection.push_back(lastLayer[i]);
}
}
for (int i=0; i<num; i++) {
tmp[i].ID = i;
tmp[i].actual_val = vals[i];
tmp[i].biasConnectino=bia;
tmp[i].linkTobias=RAND_WEIGHT();
tmp[i].vLinkTobias=0.0;
bia->outConeection.push_back(&tmp[i]);
output.push_back(&tmp[i]);
}
bias.push_back(bia);
}
double Nerual::sigmod(double sum)
{
double val = 1.0 / (1.0+exp(-sum));
return val;
}
double Nerual::Relu(double sum)
{
double val = std::max(0.0,sum);
return val;
}
double Nerual::Softmax(double sum,double f)
{
double val = f / sum;
return val;
}
double Nerual::SoftmaxDerivative(double z,double t)
{
double val = z -t;
return val;
}
double Nerual::sigmodDerivative(double act)
{
double val = act*(1-act);
return val;
}
bool Nerual::ReluDerivative(double act)
{
if(act<=0)
return 1;
return 0;
}
double Nerual::weightUpdate(double error,double act)
{
double val = n*error*act;
return val;
}
void Nerual::feedForward(int i)
{
vector<hiddenNode*> bottomLayer = hiddens[0];
//求连接input的hidden值
for (int j=0; j<bottomLayer.size(); j++)
{
double sum=0;
vector<inputNode*> inputLayer =bottomLayer[j]->intConnection;
for (int k=0; k<inputLayer.size(); k++)
{
inputLayer[k]->curr_val = inputLayer[k]->activation_vals[i];
//求值
sum += inputLayer[k]->curr_val*inputLayer[k]->linkToHidden[j];
}
sum+=bottomLayer[j]->biasConnectino->activation_vals*bottomLayer[j]->linkTobias;
//状态值
bottomLayer[j]->z = sum;
//使用激活函数
bottomLayer[j]->activation_vals = sigmod(sum);
// bottomLayer[j]->activation_vals=Relu(sum);
}
//求hidden连接hidden的值
for (int j=1; j<hiddens.size(); j++)
{
vector<hiddenNode*> nextLayer = hiddens[j];
for (int k=0; k<nextLayer.size(); k++)
{
double sum=0;
for (int z=0; z<bottomLayer.size(); z++)
{
//求值
sum+=bottomLayer[z]->activation_vals*bottomLayer[z]->linkToHO[k];
}
sum+=nextLayer[k]->biasConnectino->activation_vals*nextLayer[k]->linkTobias;
//状态值
nextLayer[k]->z = sum;
//使用激活函数
nextLayer[k]->activation_vals = sigmod(sum);
// nextLayer[k]->activation_vals = Relu(sum);
}
bottomLayer = nextLayer;
}
//求output值
tmp_sum=0;
max=0;
double *sums = new double[output.size()];
for (int j=0; j<output.size();j++)
{
double sum=0;
for (int k=0; k<bottomLayer.size(); k++)
{
sum += bottomLayer[k]->activation_vals*bottomLayer[k]->linkToHO[j];
}
sum+=output[j]->biasConnectino->activation_vals*output[j]->linkTobias;
//状态值
output[j]->z = sum;
if(max<sum)
{
max = sum;
}
sums[j]=sum;
}
//防止溢出
for (int j=0; j<output.size(); j++)
{
sums[j]-=max;
tmp_sum+=exp(sums[j]);
}
//预测值
for (int j=0; j<output.size(); j++)
{
output[j]->activation_vals = Softmax(tmp_sum,exp(sums[j]));
}
delete[] sums;
}
void Nerual::backPropagate(int i)
{
vector<hiddenNode*> upHiddenLayer= hiddens[hiddens.size()-1];
//output错误率
for (int j=0; j<output.size();j++)
{
output[j]->error=SoftmaxDerivative(output[j]->activation_vals,output[j]->actual_val[i]);
//momentum
double *v = &output[j]->vLinkTobias;
*v= alpha*(*v)-weightUpdate(output[j]->error, output[j]->biasConnectino->activation_vals);
output[j]->linkTobias += (*v);
}
//求hidden错误率
for (int j=0; j<upHiddenLayer.size(); j++)
{
double sum=0;
double *v;
vector<outputNode*> outputLayer = upHiddenLayer[j]->outConnection;
for (int k=0; k<outputLayer.size(); k++)
{
sum+=upHiddenLayer[j]->linkToHO[k]*outputLayer[k]->error;
//momentum
v = &upHiddenLayer[j]->vLinkToHO[k];
*v = alpha*(*v)-weightUpdate(output[k]->error, upHiddenLayer[j]->activation_vals);
upHiddenLayer[j]->linkToHO[k]+=(*v);
}
// upHiddenLayer[j]->error = ReluDerivative(upHiddenLayer[j]->activation_vals)==1?0:sum;
upHiddenLayer[j]->error = sigmodDerivative(upHiddenLayer[j]->activation_vals)*sum;
//momentum
v = &upHiddenLayer[j]->vLinkTobias;
*v = alpha*(*v)-weightUpdate(upHiddenLayer[j]->error, upHiddenLayer[j]->biasConnectino->activation_vals);
upHiddenLayer[j]->linkTobias+=(*v);
}
//更新hidden到hidden的错误率和权重
for (int j=static_cast<int>(hiddens.size()-2); j>=0; j--)
{
vector<hiddenNode*> downLayer= hiddens[j];
for (int k=0; k<downLayer.size(); k++)
{
double sum=0;
double *v;
for (int z=0; z<upHiddenLayer.size(); z++)
{
sum+=downLayer[k]->linkToHO[z]*upHiddenLayer[z]->error;
//momentum
v = &downLayer[k]->vLinkToHO[z];
*v = alpha*(*v)-weightUpdate( upHiddenLayer[z]->error, downLayer[k]->activation_vals);
downLayer[k]->linkToHO[z]+=(*v);
}
// downLayer[k]->error = ReluDerivative(downLayer[k]->activation_vals)==1?0:sum;
downLayer[k]->error = sigmodDerivative(downLayer[k]->activation_vals)*sum;
//momentum
v = &downLayer[k]->vLinkTobias;
*v = alpha*(*v)-weightUpdate(downLayer[k]->error, downLayer[k]->biasConnectino->activation_vals);
downLayer[k]->linkTobias+=(*v);
}
upHiddenLayer = downLayer;
}
//更新连接input的hidden的权重
for (int j=0; j<input.size(); j++)
{
double *v;
for (int k=0; k<upHiddenLayer.size(); k++)
{
//momentum
v = &input[j]->vLinkToHO[k];
*v = alpha*(*v)-weightUpdate(upHiddenLayer[k]->error, input[j]->curr_val);
input[j]->linkToHidden[k]+=(*v);
}
}
}
double Nerual::Trainning_Network()
{
double accumulatedErr=0.0;
double err;
for (int i=0; i<input[0]->activation_vals.size(); i++)
{
feedForward(i);
err = 0.0;
for (int j=0; j<output.size(); j++) {
err +=sqr(output[j]->activation_vals-output[j]->actual_val[i]);
}
err = 0.5 * err;
accumulatedErr += err;
backPropagate(i);
}
return accumulatedErr;
}
void Nerual::testOneLine(double x[])
{
for (int i=0; i<input.size(); i++)
{
input[i]->activation_vals.clear();
input[i]->activation_vals.push_back(x[i]);
}
max=0;
int iindex=0;
feedForward(0);
for (int j=0; j<output.size();j++)
{
if(max<output[j]->activation_vals)
{
max=output[j]->activation_vals;
iindex=j;
}
}
char word = iindex+65;
cout<<word<<endl;
}
void Nerual::testSet(vector<vector<double>> vals,vector<string> tital)
{
int correct=0;
for (int j=0; j<vals.size(); j++)
{
input[j]->activation_vals.clear();
input[j]->activation_vals = vals[j];
}
for (int i=0; i<tital.size(); i++)
{
feedForward(i);
max=0;
int iindex=0;
for (int j=0; j<output.size();j++)
{
if(max<output[j]->activation_vals)
{
max=output[j]->activation_vals;
iindex=j;
}
}
char word = iindex+65;
char *tw =(char*)tital[i].c_str();
if(word==*tw)
{
correct++;
}
cout<<*tw<<" "<<word<<endl;
}
cout<<"correctively: "<<correct<<endl;
}
main.cpp
#include<iostream>
#include <fstream>
#include <sstream>
#include "Nerual.hpp"
double n=0.02;
double alpha =0.02;
int HIDALOG=0;
int SIGMOID=0;
int RELU=1;
int TANH=2;
int main()
{
string myText;
stringstream ss;
ifstream MyFile("/Users/user/Desktop/intelligenc system /CNN/6/infrastructure/lib/Training_Data.txt");
string tmp;
vector<vector<double>> trainData(16);
vector<vector<double>> actualData(26);
vector<string> tital2;
for (int i=0;i<actualData.size(); i++) {
actualData[i].resize(16000);
}
int move=0;
int shift=0;
while (getline (MyFile, myText)) {
// Output the text from the file
ss.str(myText);
getline(ss,tmp,',');
tital2.push_back(tmp);
char *p = (char*)tmp.c_str();
int num = *p-65;
actualData[num][move]=1;
while (getline(ss,tmp,','))
{
trainData[shift].push_back(stoi(tmp));
shift++;
}
ss.clear();
shift=0;
move++;
}
MyFile.close();
ifstream TestFile("/Users/user/Desktop/intelligenc system /CNN/6/infrastructure/lib/Test_Data.txt");
vector<vector<double>> testData(16);
vector<string> tital;
while (getline (TestFile, myText)) {
ss.str(myText);
getline(ss,tmp,',');
tital.push_back(tmp);
while (getline(ss,tmp,','))
{
testData[shift].push_back(stoi(tmp));
shift++;
}
ss.clear();
shift=0;
}
TestFile.close();
srand(static_cast<unsigned int>(123));
Nerual *bp = new Nerual;
bp->setInputLayer(16, trainData);
bp->setInputToHidden(128);
bp->setHiddenToHidden(64);
bp->setHiddenToHidden(32);
bp->setOutToHidden(26,actualData);
double SSE=0.0;
for (int i=0; i<50; i++) {
SSE=bp->Trainning_Network();
cout<<"times "<<i << " SSE "<<SSE<<endl;
}
double t[] ={2,8,3,5,1,8,13,0,6,6,10,8,0,8,0,8};
bp->testOneLine(t);
bp->testSet(testData,tital);
return 0;
}