class BpDeep
import java.util.Random;
public class BpDeep
{
/**
* 各层节点值
*/
public double[][] layer;
/**
* 各层节点误差
*/
public double[][] layerErr;
/**
* 各层节点权重
*/
public double[][][] layer_weight;
/**
* 各层节点权重动量
*/
public double[][][] layer_weight_delta;
/**
* 动量系数
*/
public double mobp;
/**
* 学习系数/步长
*/
public double rate;
/**
* 初始化过程 <br>
* <br>
* 由于是n层神经网络,我们用二维数组layer记录节点值, <br>
* 第一维为层数,第二维为该层节点位置,数组的值为节点值。 <br>
* 同样, 节点误差值layerErr也是相似方式记录 ; <br>
* <br>
* 用三维数组layer_weight记录各节点权重, <br>
* 第一维为层数,第二维为该层节点位置,第三维为下层节点位置 , <br>
* 数组的值为某节点到达下层某节点的权重值,初始值为0-1之间的随机数。 <br>
* 为了优化收敛速度,这里采用动量法权值调整,需要记录上一次权值调整量; <br>
* <br>
* 用三维数组layer_weight_delta来记录,截距项处理: <br>
* 程序里将截距的值设置为1,这样只需要计算它的权重就可以了。
*/
public BpDeep(int[] layernum, double rate, double mobp)
{
this.mobp = mobp;
this.rate = rate;
Random random = new Random();
layer = new double[layernum.length][];
layerErr = new double[layernum.length][];
layer_weight = new double[layernum.length][][];
layer_weight_delta = new double[layernum.length][][];
for (int l = 0; l < layernum.length; l++)
{
layer[l] = new double[layernum[l]];
layerErr[l] = new double[layernum[l]];
if (l + 1 < layernum.length)
{
layer_weight[l] = new double[layernum[l] + 1][layernum[l + 1]];
layer_weight_delta[l] = new double[layernum[l] + 1][layernum[l + 1]];
for (int j = 0; j < layernum[l] + 1; j++)
for (int i = 0; i < layernum[l + 1]; i++)
layer_weight[l][j][i] = random.nextDouble();
}
}
}
/**
* 逐层向前计算输出<br>
* <br>
* 采用S函数1/(1+Math.exp(-z))将每个节点的值统一到0-1之间,<br>
* 再逐层向前计算直到输出层,对于输出层,实际上是不需要再用S函数的,<br>
* 我们这里将输出结果视为0到1之间的概率值,所以也采用了S函数,这样也有利于程序实现的统一性。
*/
public double[] computeOut(double[] in)
{
for (int l = 1; l < layer.length; l++)
{
for (int j = 0; j < layer[l].length; j++)
{
double z = layer_weight[l - 1][layer[l - 1].length][j];
for (int i = 0; i < layer[l - 1].length; i++)
{
layer[l - 1][i] = l == 1 ? in[i] : layer[l - 1][i];
z += layer_weight[l - 1][i][j] * layer[l - 1][i];
}
layer[l][j] = 1 / (1 + Math.exp(-z));
}
}
return layer[layer.length - 1];
}
/**
* 逐层反向计算误差并修改权重<br>
* <br>
* 神经网络如何计算误差,一般采用平方型误差函数E,<br>
* 也就是将多个输出项和对应目标值的误差的平方累加起来,再除以2。<br>
* 实际上逻辑回归的误差函数也是这个,至于为什么要用这个函数来计算误差,<br>
* 它从数学上的合理性是什么 ,怎么得来的,<br>
* 这个我建议程序员们不想当数学家的话,先不去深究了,<br>
* 现在我们要做的是如何把这个函数E误差取它的最小值,需要对其进行求导。<br>
* <br>
* 在我们的程序里用layerErr记录了E对权重求导后的最小化误差,<br>
* 再根据最小化误差去调整权重。<br>
* 注意这里采用动量法调整,将上一次调整的经验考虑进来,避免陷入局部最小值,<br>
* 下面的k代表迭代次数,mobp为动量项,rate为学习步长:<br>
* Δw(k+1) = mobp*Δw(k)+rate*Err*Layer<br>
* 也有很多使用下面的公式,效果上的差别不是太大:<br>
* Δw(k+1) = mobp*Δw(k)+(1-mobp)rate*Err*Layer<br>
* <br>
* 为了提升性能,注意程序实现是在一个while里面同时计算误差和调整权重,<br>
* 先将位置定位到倒数第二层(也就是最后一层隐含层)上,然后逐层反向调整,<br>
* 根据L+1层算好的误差来调整L层的权重,同时计算好L层的误差,<br>
* 用于下一次循环到L-1层时计算权重,以此循环下去直到倒数第一层(输入层)结束。
*/
private void updateWeight(double[] tar)
{
int l = layer.length - 1;
for (int j = 0; j < layerErr[l].length; j++)
layerErr[l][j] = layer[l][j] * (1 - layer[l][j])
* (tar[j] - layer[l][j]);
while (l-- > 0)
{
for (int j = 0; j < layerErr[l].length; j++)
{
double z = 0.0;
for (int i = 0; i < layerErr[l + 1].length; i++)
{
z = z + l > 0 ? layerErr[l + 1][i] * layer_weight[l][j][i]
: 0;
layer_weight_delta[l][j][i] = mobp
* layer_weight_delta[l][j][i] + rate
* layerErr[l + 1][i] * layer[l][j];
layer_weight[l][j][i] += layer_weight_delta[l][j][i];
if (j == layerErr[l].length - 1)
{
layer_weight_delta[l][j + 1][i] = mobp
* layer_weight_delta[l][j + 1][i] + rate
* layerErr[l + 1][i];
layer_weight[l][j + 1][i] += layer_weight_delta[l][j + 1][i];
}
}
layerErr[l][j] = z * layer[l][j] * (1 - layer[l][j]);
}
}
}
/**
* 迭代训练<br>
* <br>
* 在整个迭代训练计算过程中,节点的值是每次计算都在变化的,<br>
* 不需要保存,而权重参数和误差参数是需要保存的,需要为下一次迭代提供支持
*/
public void train(double[] in, double[] tar)
{
computeOut(in);
updateWeight(tar);
}
}
abstract class BaseTest
package test;
import src.BpDeep;
public abstract class BaseTest
{
protected int test_sum = 0;
protected int true_sum = 0;
protected BpDeep bp;
public BaseTest test()
{
return test(false);
}
public abstract BaseTest test(boolean needPrint);
public abstract void log(double[] x, double[] ans, boolean needPrint);
public BpDeep getBp()
{
return bp;
}
public float getRate()
{
float rate = (float) true_sum * 100 / (float) test_sum;
System.out.println("test_sum:" + test_sum);
System.out.println("true_sum:" + true_sum);
System.out.println("success rate:" + rate + "%");
return rate;
}
public int getTest_sum()
{
return test_sum;
}
public int getTrue_sum()
{
return true_sum;
}
}
class MainTest
package test;
import src.BpDeep;
import com.alibaba.fastjson.JSON;
public class MainTest
{
public static void main(String[] args)
{
test1();
}
public static void test2()
{
BpDeep bp = new BpDeep(new int[]
{ 2, 7, 1 }, 0.15, 0.5);
BaseTest bt = new YNTest(bp);
try
{
bt.bp.layer_weight = JSON
.parseArray(
"["
+ "[[[2.882463044063628,0.9835401561176416,0.47150033573011396,0.4737770272602944,5.242374865834606,0.4874033655930285,0.4539790814245602],[3.6799786001461516,-4.143363053947223,0.5189148421407987,0.5231669550480109,-1.132248537664744,0.5395317010418201,0.5001639089491579],[-4.231996276448272,0.2096327796153145,0.325713181786105,0.31654286258941117,-0.6758360632886017,0.06874837042860536,0.8459677249240193]],[[5.508696104507275],[-8.729341228429641],[-1.4298740399535579],[-1.6221106787524626],[8.687573994845355],[-1.5048561928217428],[-2.303505490168803],[-3.128582659146966]],null]"
+ "]", double[][][].class).get(0);
bt.bp.layer_weight_delta = JSON
.parseArray(
"["
+ "[[[-1.9957269125642678E-8,2.1508303541742595E-7,9.579786779255544E-7,1.0775896894071114E-6,3.8311239308688384E-8,1.1427865339644673E-6,1.0672687018315496E-6],[-1.8861214174816247E-7,-4.0988512826961735E-8,2.1871137992461507E-6,2.433174148867432E-6,-3.0625262122260975E-7,2.563633758748932E-6,2.42209280880393E-6],[-2.9045750469405977E-7,1.5362120106170125E-7,4.867722628606334E-7,5.443102480186127E-7,-2.0695106720425574E-7,5.697399075286526E-7,5.509803023389848E-7]],[[-1.3072300578468227E-5],[-5.387228259465918E-6],[-1.2913773180821744E-5],[-1.2918631784080454E-5],[-1.2908665806221774E-6],[-1.286609485682395E-5],[-1.3029444473638176E-5],[-1.3281927482953673E-5]],null]"
+ "]", double[][][].class).get(0);
double[][] x = new double[][]
{
{ 333, 442 },
{ 123, 0 } };
for (int j = 0; j < x.length; j++)
bt.log(x[j], bp.computeOut(x[j]), true);
} catch (Exception e)
{
e.printStackTrace();
}
}
public static void test1()
{
int ans_sum = 10;
int test_sum = 0;
float rate_sum = 0;
BaseTest bt = new CheckTest();
for (int i = 0; i < ans_sum; i++)
{
bt.test(true);
test_sum += bt.getTest_sum();
rate_sum += bt.getRate();
}
float rate = rate_sum / ans_sum;
System.out.println("-------------------------");
System.out.println("ans_sum:" + ans_sum);
System.out.println("test_sum:" + test_sum);
System.out.println("rate_sum:" + rate_sum);
System.out.println("success rate:" + rate + "%");
}
public static void printArray(double[][] arrs)
{
if (null != arrs)
for (double[] arr : arrs)
{
if (null != arr)
for (double d : arr)
{
System.out.print(d + "-");
}
System.out.println();
}
}
public static void printArray(double[][][] arrs)
{
for (double[][] arr : arrs)
{
if (null != arr)
printArray(arr);
}
}
}
class YNTest extends BaseTest
package test;
import java.util.Arrays;
import src.BpDeep;
public class YNTest extends BaseTest
{
public YNTest()
{
bp = new BpDeep(new int[]
{ 2, 7, 1 }, 0.15, 0.5);
}
public YNTest(BpDeep bd)
{
bp = bd;
}
public BaseTest test(boolean needPrint)
{
double[][] data = new double[][]
{
{ 1, 1 },
{ 1, 0 },
{ 0, 1 },
{ 0, 0 },
{ 9, 8 },
{ 8, 0 },
{ 0, 7 },
{ 0, 0 } };
double[][] target = new double[][]
{
{ 1 },
{ 0 },
{ 0 },
{ 0 },
{ 1 },
{ 0 },
{ 0 },
{ 0 } };
for (int n = 0; n < 2000; n++)
for (int i = 0; i < data.length; i++)
bp.train(data[i], target[i]);
for (int j = 0; j < data.length; j++)
log(data[j], bp.computeOut(data[j]), needPrint);
double[][] x = new double[][]
{
{ 3, 4 },
{ 6, 0 } };
for (int j = 0; j < x.length; j++)
log(x[j], bp.computeOut(x[j]), needPrint);
return this;
}
public void log(double[] x, double[] ans, boolean needPrint)
{
boolean bool = false;
if (0 < x[0] && 0 < x[1] && 5 < ans[0] * 10)
{
bool = true;
}
else if ((1 > x[0] || 1 > x[1]) && 5 > ans[0] * 10)
{
bool = true;
}
if (needPrint)
System.out.println(Arrays.toString(x) + ":" + Arrays.toString(ans)
+ "-" + bool);
test_sum += 1;
if (bool)
true_sum += 1;
}
}
class XYTest extends BaseTest
package test;
import java.util.Arrays;
import src.BpDeep;
public class XYTest extends BaseTest
{
public XYTest()
{
bp = new BpDeep(new int[]
{ 2, 10, 2 }, 0.15, 0.8);
}
public BaseTest test(boolean needPrint)
{
double[][] data = new double[36][2];
for (int i = 0; i < 36; i += 4)
{
data[i][0] = -i - 1;
data[i][1] = i + 1;
data[i + 1][0] = i + 1;
data[i + 1][1] = i + 1;
data[i + 2][0] = -i - 1;
data[i + 2][1] = -i - 1;
data[i + 3][0] = i + 1;
data[i + 3][1] = -i - 1;
}
double[][] target = new double[36][2];
for (int i = 0; i < 36; i += 4)
{
target[i][0] = 0;
target[i][1] = 1;
target[i + 1][0] = 1;
target[i + 1][1] = 1;
target[i + 2][0] = 0;
target[i + 2][1] = 0;
target[i + 3][0] = 1;
target[i + 3][1] = 0;
}
for (int n = 0; n < 5000; n++)
for (int i = 0; i < data.length; i++)
bp.train(data[i], target[i]);
for (int j = 0; j < data.length; j++)
log(data[j], bp.computeOut(data[j]), needPrint);
double[][] x = new double[][]
{
{ -4, 5 },
{ 3, 1 },
{ -2, 3 },
{ 5, -1 } };
for (int j = 0; j < x.length; j++)
log(x[j], bp.computeOut(x[j]), needPrint);
return this;
}
public void log(double[] x, double[] ans, boolean needPrint)
{
boolean bool = false;
if (0 < x[0] && 0 < x[1] && 5 < ans[0] * 10 && 5 < ans[1] * 10)
{
bool = true;
}
else if (0 > x[0] && 0 < x[1] && 5 > ans[0] * 10 && 5 < ans[1] * 10)
{
bool = true;
}
else if (0 > x[0] && 0 > x[1] && 5 > ans[0] * 10 && 5 > ans[1] * 10)
{
bool = true;
}
else if (0 < x[0] && 0 > x[1] && 5 < ans[0] * 10 && 5 > ans[1] * 10)
{
bool = true;
}
if (needPrint)
System.out.println(Arrays.toString(x) + ":" + Arrays.toString(ans)
+ "-" + bool);
test_sum += 1;
if (bool)
true_sum += 1;
}
}
class OETest extends BaseTest
package test;
import java.util.Arrays;
import java.util.Random;
import src.BpDeep;
public class OETest extends BaseTest
{
public OETest()
{
bp = new BpDeep(new int[]
{ 32, 20, 2 }, 0.25, 0.5);
}
public BaseTest test(boolean needPrint)
{
double[][] data = new double[1000][];
double[][] data2 = new double[data.length][1];
for (int i = 0; i < data.length; i++)
{
data2[i][0] = i * (0 == new Random().nextInt(10) % 2 ? -1 : 1);
data[i] = getInt32((int) data2[i][0]);
}
double[][] target = new double[data.length][2];
for (int i = 0; i < data.length; i++)
{
target[i][0] = 0 == data2[i][0] % 2 ? 0 : 1;
target[i][1] = 0 > data2[i][0] ? 0 : 1;
}
for (int n = 0; n < 200; n++)
for (int i = 0; i < data.length; i++)
bp.train(data[i], target[i]);
for (int j = 0; j < data.length; j++)
log(data2[j], bp.computeOut(data[j]), needPrint);
double[][] x = new double[][]
{
{ 13462 },
{ 2631 },
{ -4123 },
{ -532 } };
for (int j = 0; j < x.length; j++)
log(x[j], bp.computeOut(getInt32((int) x[j][0])), needPrint);
return this;
}
public void log(double[] x, double[] ans, boolean needPrint)
{
boolean bool = false;
if (((0 != x[0] % 2 && 5 < ans[0] * 10) || (0 == x[0] % 2 && 5 > ans[0] * 10))
&& ((0 < x[0] && 5 < ans[1] * 10) || (0 > x[0] && 5 > ans[1] * 10)))
{
bool = true;
}
if (needPrint)
System.out.println(Arrays.toString(x) + ":" + Arrays.toString(ans)
+ "-" + bool);
test_sum += 1;
if (bool)
true_sum += 1;
}
private double[] getInt32(int val)
{
double[] data = new double[32];
int index = 31;
do
{
data[index--] = (val & 1);
val >>>= 1;
} while (val != 0);
return data;
}
}