需要对28x28的灰度数字图像进行数字识别。 输入层 28x28个输入节点,分别代表像素点上的灰度大小 输出层10个输出,分别代表0-1的概率
使用sigmoid函数
由于求导过于繁琐,因此这里仅添加了一层隐藏层。
代码如下
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
using namespace std;
char file[]="train-images-idx3-ubyte";
char lc;
#define gc() (lc=getchar())
#define sigmoid(x) (1/(1+exp(x)))
int read(int&x)
{
x=0;
char c;
do c=gc();while((c!=EOF)&&(c<'0'||c>'9')&&(c<'a'||c>'z')&&(c<'A'||c>'Z'));
if (lc==EOF)
{
x=-1;
return -1;
}
while(!((c<'0'||c>'9')&&(c<'a'||c>'z')&&(c<'A'||c>'Z')))
{
x=x*10+c-'0';
c=gc();
}
return 1;
}
int n_lay1=28*28;
double w1[28*28][20];
double dw1[28*28][20];
double b1[20];
double db1[20];
double x1[28*28];
double sig1[28*28];
int n_lay2=20;
double w2[20][10];
double dw2[20][10];
double b2[10];
double db2[10];
double mid[60001][20];
double dmid[20];
double out[60001][10];
double train[60001][28*28];
double label[60001][10];
int train_N;
double get_loss(const int &train_n)
{
double loss=0;
for(int i=0;i<train_n;i++)
{
for(int j=0;j<20;j++)
mid[i][j]=b1[j];
for(int j=0;j<28*28;j++)
for(int k=0;k<20;k++)
{
mid[i][k]=mid[i][k]+w1[j][k]*train[i][j];
}
for(int j=0;j<20;j++)
mid[i][j]=sigmoid(mid[i][j]);
for(int j=0;j<10;j++)
out[i][j]=b2[j];
for(int j=0;j<20;j++)
for(int k=0;k<10;k++)
out[i][k]=out[i][k]+w2[j][k]*mid[i][j];
for(int j=0;j<10;j++)
out[i][j]=sigmoid(out[i][j]);
for(int j=0;j<10;j++)
{
loss=loss+0.5*(label[i][j]-out[i][j])*(label[i][j]-out[i][j]);
if(isnan(loss))
{
printf("NAN!!!\n");
printf("i:%d j:%d label:%lf out:%lf",i,j,label[i][j],out[i][j]);
return 0;
}
}
}
puts("loss_ok");
return loss;
}
int main()
{
char tmp;
int cnt=0;
int test_set=1;
freopen("train_data.in","r",stdin);
read(train_N);
int n,m;
read(n),read(m);
int train_n=train_N;
printf("train_n=%d\n",train_n);
int tmpt;
for(int i=0;i<train_n;i++)
for (int j=0;j<n*m;j++)
read(tmpt),train[i][j]=tmpt/256.0;
freopen("train_label.in","r",stdin);
read(n);
for(int i=0;i<train_n;i++)
{
int l;
read(l);
label[i][l]=1;
}
srand(100);
for(int i=0;i<28*28;i++)
for(int j=0;j<20;j++)
w1[i][j]=((rand()&2)-1)*0.05*log((1+log(rand()))),b1[j]=((rand()&2)-1)*0.01*log((1+log(rand())));
for(int i=0;i<20;i++)
for(int j=0;j<10;j++)
w2[i][j]=((rand()&2)-1)*0.05*log((1+log(rand()))),b2[j]=((rand()&2)-1)*0.01*log((1+log(rand())));
double new_los=0;
double old_loss=1e100;
double loss=0;
double best_loss=old_loss;
double alpha=1;
while(alpha>=1e-7)
{
loss=get_loss(train_n);
printf("%lf\n",loss);
if(loss>old_loss)
alpha=alpha/2;
else if(loss<best_loss)
{
best_loss=loss;
FILE*fout;
fout=fopen("train_model.out","w");
for(int i=0;i<28*28;i++,fprintf(fout,"\n"))
for(int j=0;j<20;j++)
fprintf(fout,"%lf,",w1[i][j]);
for(int j=0;j<20;j++)
fprintf(fout,"%lf,",b1[j]);
fprintf(fout,"\n");
for(int i=0;i<20;i++,fprintf(fout,"\n"))
for(int j=0;j<10;j++)
fprintf(fout,"%lf,",w2[i][j]);
for(int j=0;j<10;j++)
fprintf(fout,"%lf,",b2[j]);
fprintf(fout,"\n");
fclose(fout);
}
old_loss=loss;
for(int j=0;j<10;j++)
{
db2[j]=0;
for(int i=0;i<train_n;i++)
db2[j]+=(label[i][j]-out[i][j])*(1/out[i][j]-1)*out[i][j]*out[i][j];
}
puts("db2 ok");
for(int j=0;j<10;j++)
for(int k=0;k<20;k++)
{
dw2[k][j]=0;
for(int i=0;i<train_n;i++)
dw2[k][j]+=(label[i][j]-out[i][j])*(1/out[i][j]-1)*mid[i][k]*out[i][j]*out[i][j];
}
puts("dw2 ok");
for (int t=0;t<28*28;t++)
for(int k=0;k<20;k++)
dw1[t][k]=0;
double tmp_ij=0,tmp_ijk;
for(int j=0;j<10;j++)
{
for(int i=0;i<train_n;i++)
{
tmp_ij=(label[i][j]-out[i][j])*(1/out[i][j]-1)*out[i][j]*out[i][j];
for(int k=0;k<20;k++)
{
tmp_ijk=tmp_ij*w2[k][j]*(1/mid[i][k]-1)*mid[i][k]*mid[i][k];
for (int t=0;t<28*28;t+=4)
{
dw1[t][k]-=tmp_ijk*train[i][t];
dw1[t+1][k]-=tmp_ijk*train[i][t+1];
dw1[t+2][k]-=tmp_ijk*train[i][t+2];
dw1[t+3][k]-=tmp_ijk*train[i][t+3];
}
}
}
}
puts("dw1 ok");
for (int k=0;k<20;k++)
{
db1[k]=0;
for(int j=0;j<10;j++)
{
for(int i=0;i<train_n;i++)
db1[k]-=(label[i][j]-out[i][j])*(1/out[i][j]-1)*w2[k][j]*out[i][j]*out[i][j]
*(1/mid[i][k]-1)*(mid[i][k]*mid[i][k]);
}
}
puts("db1 ok");
int cnt_i=28*10+10;
double dw=w1[cnt_i][2]*1e-2;
w1[cnt_i][2]+=dw;
double dLoss=get_loss(train_n)-loss;
printf("dw1:%lf dLoss:%lf div:%lf derive:%lf\n",dw,dLoss,dLoss/dw,dw1[cnt_i][2]);
w1[cnt_i][2]-=dw;
dw=w2[10][2]*1e-2;
w2[10][2]+=dw;
dLoss=get_loss(train_n)-loss;
printf("dw2:%lf dLoss:%lf div:%lf derive:%lf\n",dw,dLoss,dLoss/dw,dw2[10][2]);
w2[10][2]-=dw;
double db=b1[10]*1e-2;
b1[10]+=db;
dLoss=get_loss(train_n)-loss;
printf("db1:%lf dLoss:%lf div:%lf derive:%lf\n",db,dLoss,dLoss/db,db1[10]);
b1[10]-=db;
db=b2[5]*1e-6;
b2[5]+=db;
dLoss=get_loss(train_n)-loss;
printf("db2:%lf dLoss:%lf div:%lf derive:%lf\n",db,dLoss,dLoss/db,db2[5]);
b2[5]-=db;
double tmp=0;
for(int i=0;i<28*28;i++)
for(int j=0;j<20;j++)
tmp+=dw1[i][j]*dw1[i][j];
for(int j=0;j<20;j++)
tmp+=db1[j]*db1[j];
for(int i=0;i<20;i++)
for(int j=0;j<10;j++)
tmp+=dw2[i][j]*dw2[i][j];
for(int j=0;j<10;j++)
tmp+=db2[j]*db2[j];
double dif=0.0001*alpha;
printf(" dif:%lf\n",dif);
printf(" tmp:%lf\n",tmp);
for(int i=0;i<28*28;i++)
for(int j=0;j<20;j++)
w1[i][j]-=dif*dw1[i][j];
for(int j=0;j<20;j++)
b1[j]-=dif*db1[j];
for(int i=0;i<20;i++)
for(int j=0;j<10;j++)
w2[i][j]-=dif*dw2[i][j];
for(int j=0;j<10;j++)
b2[j]-=dif*db2[j];
{
FILE*fout;
fout=fopen("tmp3.out","w");
for(int i=0;i<28*28;i++,fprintf(fout,"\n"))
for(int j=0;j<20;j++)
fprintf(fout,"%lf,",w1[i][j]);
for(int j=0;j<20;j++)
fprintf(fout,"%lf,",b1[j]);
fprintf(fout,"\n");
for(int i=0;i<20;i++,fprintf(fout,"\n"))
for(int j=0;j<10;j++)
fprintf(fout,"%lf,",w2[i][j]);
for(int j=0;j<10;j++)
fprintf(fout,"%lf,",b2[j]);
fprintf(fout,"\n");
fclose(fout);
}
}
return 0;
}
由于机器实在太差,没有GPU,于是在代码中进行了一些小优化。 如减少
t
m
p
i
j
k
tmp_{ijk}
tmpijk? 与
t
m
p
i
j
tmp_{ij}
tmpij?的计,同时通过循环重构,压榨了一下多核性能。
不得不说,看着自己的深度智障慢慢的会认字了,还是很开心的。
|