虽然几乎没有多少人用梯度下降法
为了降低误差,所有变量最好都用 long double。学习率要设置衰减,但不能降得太快(本蒟蒻学习率初始 0.5,衰减率 0.99995,总共训练 100000 个 epoch)。另外,注意卡常。
如果代码过不去,可以按照本蒟蒻的这份代码调一下参。
#include<bits/stdc++.h>
using namespace std;
const long double eps=1e-6;
int n;
long double a[15][15];
long double pos[15];
long double dis[15];
inline long double calcLoss(){
register double loss=0,avg=0;
for(register int i=1;i<=n+1;i++){
double sqdis=0;
for(register int j=1;j<=n;j++){
sqdis+=pow(a[i][j]-pos[j],2);
}
dis[i]=sqrt(sqdis);
avg+=dis[i];
}
avg/=(n+1);
for(register int i=1;i<=n+1;i++){
loss+=pow(dis[i]-avg,2);
}
return loss;
}
inline long double calcGrad(int dim){
register long double sample1,sample2;
pos[dim]+=eps;
sample2=calcLoss();
pos[dim]-=eps*2;
sample1=calcLoss();
pos[dim]+=eps;
return (sample2-sample1)/(2*eps);
}
inline void backward(const double &lr=1e-3){
register long double grad[15];
for(register int i=1;i<=n;i++){
grad[i]=calcGrad(i);
}
for(register int i=1;i<=n;i++){
pos[i]-=grad[i]*lr;
}
}
int main(){
cin>>n;
for(register int i=1;i<=n+1;i++){
for(register int j=1;j<=n;j++){
cin>>a[i][j];
}
}
for(int i=1;i<=n;i++)pos[i]=a[1][i];
double lr=0.5;
for(int i=1;i<=100000;i++){
// if(i%10000==0){
// cout<<"Iteration "<<i<<":Loss "<<calcLoss()<<endl;
// cout<<"Grad:";
// for(int j=1;j<=n;j++)cout<<calcGrad(j)<<" ";
// cout<<endl;
// }
backward(lr);
lr*=0.99995;
}
for(int i=1;i<=n;i++){
cout<<fixed<<setprecision(3)<<pos[i]<<" ";
}
}