java BpDeep

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 = 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 + "%");

        // System.out.println("-------layer------------------");
        // printArray(bt.getBp().layer);
        // System.out.println("-------layerErr------------------");
        // printArray(bt.getBp().layerErr);
        // System.out.println("-------layer_weight------------------");
        // printArray(bt.getBp().layer_weight);
        // System.out.println("-------layer_weight_delta------------------");
        // printArray(bt.getBp().layer_weight_delta);

        // System.out.println("-------layer------------------");
        // System.out.println(JSON.toJSONString(bt.getBp().layer));
        // System.out.println("-------layerErr------------------");
        // System.out.println(JSON.toJSONString(bt.getBp().layerErr));
        // System.out.println("-------layer_weight------------------");
        // System.out.println(JSON.toJSONString(bt.getBp().layer_weight));
        // System.out.println("-------layer_weight_delta------------------");
        // System.out.println(JSON.toJSONString(bt.getBp().layer_weight_delta));
    }

    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
{
    // 初始化神经网络的基本配置
    // 第一个参数是一个整型数组,表示神经网络的层数和每层节点数,
    // 比如{3,10,10,10,10,2}表示输入层是3个节点,输出层是2个节点,中间有4层隐含层,每层10个节点
    // 第二个参数是学习步长,第三个参数是动量系数
    public XYTest()
    {
        bp    = new BpDeep(new int[]
                { 2, 10, 2 }, 0.15, 0.8);
    }

    public BaseTest test(boolean needPrint)
    {
        // 设置样本数据,对应上面的4个二维坐标数据
        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;
        }

        // 设置目标数据,对应4个坐标数据的分类
        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;
        }

        // 迭代训练5000次
        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;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值