@natsumi 2017-01-22T15:42:12.000000Z 字数 14261 阅读 1012

# Encog中的RBFNetwork

机器学习

## 1. 构造函数

### 1.1 无参构造函数

    public RBFNetwork() {        this.flat = new FlatNetworkRBF();    }

### 1.2 默认RBF中心和宽度的构造函数

    /**     * Construct RBF network.     *      * @param inputCount     *            The input count.     * @param hiddenCount     *            The hidden count.     * @param outputCount     *            The output count.     * @param t     *            The RBF type.     */    public RBFNetwork(final int inputCount, final int hiddenCount,            final int outputCount, final RBFEnum t) {        if (hiddenCount == 0) {            throw new NeuralNetworkError(                    "RBF network cannot have zero hidden neurons.");        }        final RadialBasisFunction[] rbf = new RadialBasisFunction[hiddenCount];        // Set the standard RBF neuron width.将径向基函数的宽度设为一个默认值        // Literature seems to suggest this is a good default value.        final double volumeNeuronWidth = 2.0 / hiddenCount;        //根据输入层、隐层、输出层的节点数构造FlatNetwork，RBFNetwork只是一个封装，实际的网络在成员flat中        this.flat = new FlatNetworkRBF(inputCount, rbf.length, outputCount, rbf);        try {            // try this            //设置径向基函数            setRBFCentersAndWidthsEqualSpacing(-1, 1, t, volumeNeuronWidth,                    false);        } catch (final EncogError ex) {            // if we have the wrong number of hidden neurons, try this            randomizeRBFCentersAndWidths(-1, 1, t);        }    }

    /**     * Equally spaces all hidden neurons within the n dimensional variable     * space.     *      * @param minPosition     *            The minimum position neurons should be centered. Typically 0.     * @param maxPosition     *            The maximum position neurons should be centered. Typically 1     * @param volumeNeuronRBFWidth     *            The neuron width of neurons within the mesh.     * @param useWideEdgeRBFs     *            Enables wider RBF's around the boundary of the neuron mesh.     */    public void setRBFCentersAndWidthsEqualSpacing(final double minPosition,            final double maxPosition, final RBFEnum t,            final double volumeNeuronRBFWidth, final boolean useWideEdgeRBFs) {        final int totalNumHiddenNeurons = this.flat.getRBF().length;        final int dimensions = getInputCount();        //minPosition和maxPosition表示的其实是每一维的最小和最大的中心，相减就是待等分的距离。构造函数中调用时传入参数为-1和1，disMinMaxPosition = 2        final double disMinMaxPosition = Math.abs(maxPosition - minPosition);        // Check to make sure we have the correct number of neurons for the        // provided dimensions        //由于是等分dimensions维空间，所以隐层节点数（也就是等分后的块数）= expectedSideLength^dimensions，expectedSideLength表示每一维的分割点数（比如disMinMaxPosition被三等分，则有3+1 = 4个分割点）。        final int expectedSideLength = (int) Math.pow(totalNumHiddenNeurons,                1.0 / dimensions);        final double cmp = Math.pow(totalNumHiddenNeurons, 1.0 / dimensions);        if (expectedSideLength != cmp) {            throw new NeuralNetworkError(                    "Total number of RBF neurons must be some integer to the power of 'dimensions'.\n"                            + Format.formatDouble(expectedSideLength, 5)                            + " <> " + Format.formatDouble(cmp, 5));        }        //volumeNeuronRBFWidth传入值为2.0/hidden count        final double edgeNeuronRBFWidth = 2.5 * volumeNeuronRBFWidth;        final double[][] centers = new double[totalNumHiddenNeurons][];        final double[] widths = new double[totalNumHiddenNeurons];        //循环计算每个隐层神经元的中心和宽度        for (int i = 0; i < totalNumHiddenNeurons; i++) {            centers[i] = new double[dimensions];            //SideLength表示每一维的分割点数            final int sideLength = expectedSideLength;            // Evenly distribute the volume neurons.            int temp = i;            // First determine the centers            for (int j = dimensions; j > 0; j--) {                // i + j * sidelength + k * sidelength ^2 + ... l * sidelength ^                // n                // i - neuron number in x direction, i.e. 0,1,2,3                // j - neuron number in y direction, i.e. 0,1,2,3                // Following example assumes sidelength of 4                // e.g Neuron 5 - x position is (int)5/4 * 0.33 = 0.33                // then take modulus of 5%4 = 1                // Neuron 5 - y position is (int)1/1 * 0.33 = 0.33                centers[i][j - 1] = ((int) (temp / Math.pow(sideLength, j - 1)) * (disMinMaxPosition / (sideLength - 1)))                        + minPosition;                temp = temp % (int) (Math.pow(sideLength, j - 1));            }            // Now set the widths            boolean contains = false;            for (int z = 0; z < centers[0].length; z++) {                //或许这样更好                //if ((centers[i][z] == minPosition) || (centers[i][z] == maxPosition)) {                if ((centers[i][z] == 1.0) || (centers[i][z] == 0.0)) {                    contains = true;                }            }            //useWideEdgeRBFs：布尔型，表示在neuron网格边缘使用更宽的RBF            //所以cotains应该是表示节点i是否在边缘            if (contains && useWideEdgeRBFs) {                widths[i] = edgeNeuronRBFWidth;            } else {                widths[i] = volumeNeuronRBFWidth;            }        }        setRBFCentersAndWidths(centers, widths, t);    }

                // Following example assumes sidelength of 4                // e.g Neuron 5 - x position is (int)5/4 * 0.33 = 0.33                center[5][1] = (int)5/4 * 0.33 = 0.33                // then take modulus of 5%4 = 1                temp = 5%4 = 1                // Neuron 5 - y position is (int)1/1 * 0.33 = 0.33                center[5][0] = (int)1/1 * 0.33 = 0.33

### 1.3 定制RBF构造函数

    /**     * Construct RBF network.     *      * @param inputCount     *            The input count.     * @param outputCount     *            The output count.     * @param rbf     *            The RBF type.     */    public RBFNetwork(final int inputCount, final int outputCount,            final RadialBasisFunction[] rbf) {        this.flat = new FlatNetworkRBF(inputCount, rbf.length, outputCount, rbf);        this.flat.setRBF(rbf);    }

    /**     * Array containing center position. Row n contains centers for neuron n.     * Row n contains x elements for x number of dimensions.     *      * @param centers     *            The centers.     * @param widths     *            Array containing widths. Row n contains widths for neuron n.     *            Row n contains x elements for x number of dimensions.     * @param t     *            The RBF Function to use for this layer.     */    public void setRBFCentersAndWidths(final double[][] centers,            final double[] widths, final RBFEnum t) {        for (int i = 0; i < this.flat.getRBF().length; i++) {            setRBFFunction(i, t, centers[i], widths[i]);        }    }

## 2. 网络权值的设置

### 2.1 reset函数

RBFNetwork中提供了两个reset函数用于重置（随机初始化）权值

    /**     * Reset the weights.     */    @Override    public void reset() {        (new RangeRandomizer(-1, 1)).randomize(this);    }    /**     * Reset the weights with a seed.     */    @Override    public void reset(int seed) {        ConsistentRandomizer randomizer = new ConsistentRandomizer(-1, 1, seed);        randomizer.randomize(this);    }

this的类型是RBFNetwork，继承了BasicML 并实现了MLError, MLRegression, ContainsFlat, MLResettable, MLEncodable这些接口。所以属于instanceof MLEncodable

//BasicRandomizer.java    /**     * Randomize the synapses and biases in the basic network based on an array,     * modify the array. Previous values may be used, or they may be discarded,     * depending on the randomizer.     *      * @param method     *            A network to randomize.     */    @Override    public void randomize(final MLMethod method) {        if (method instanceof BasicNetwork) {            final BasicNetwork network = (BasicNetwork) method;            for (int i = 0; i < network.getLayerCount() - 1; i++) {                randomize(network, i);            }        } else if (method instanceof MLEncodable) {//RBFNetwork属于这一类            final MLEncodable encode = (MLEncodable) method;            final double[] encoded = new double[encode.encodedArrayLength()];            encode.encodeToArray(encoded);            randomize(encoded);            encode.decodeFromArray(encoded);        }    }

//BasicRandomizer.java    /**     * Randomize the array based on an array, modify the array. Previous values     * may be used, or they may be discarded, depending on the randomizer.     *      * @param d     *            An array to randomize.     */    @Override    public void randomize(final double[] d) {        randomize(d, 0, d.length);    }    /**     * Randomize the array based on an array, modify the array. Previous values     * may be used, or they may be discarded, depending on the randomizer.     *      * @param d     *            An array to randomize.     * @param begin     *            The beginning element of the array.     * @param size     *            The size of the array to copy.     */    @Override    public void randomize(final double[] d, final int begin,                 final int size) {        for (int i = 0; i < size; i++) {            d[begin + i] = randomize(d[begin + i]);        }    }

//RangeRandomizer.java    /**     * Generate a random number based on the range specified in the constructor.     *      * @param d     *            The range randomizer ignores this value.     * @return The random number.     */    public double randomize(final double d) {        return nextDouble(this.min, this.max);    }

### 2.2 encodeToArray函数

/** * Defines a Machine Learning Method that can be encoded to a double array.   * This is very useful for certain training, such as genetic algorithms  * and simulated annealing.  * */public interface MLEncodable extends MLMethod {    /**     * @return The length of an encoded array.     */    int encodedArrayLength();    /**     * Encode the object to the specified array.     * @param encoded The array.     */    void encodeToArray(double[] encoded);    /**     * Decode an array to this object.     * @param encoded The encoded array.     */    void decodeFromArray(double[] encoded);}

    /**     * 计算数组长度     */    @Override    public int encodedArrayLength() {        int result = this.getFlat().getWeights().length;//所有的权值        for (RadialBasisFunction rbf : flat.getRBF()) {            result += rbf.getCenters().length + 1;//加上每个rbf的中心和宽度        }        return result;    }

decode函数就是与encode相反的工作～

    /**     * {@inheritDoc}     */    @Override    public void encodeToArray(double[] encoded) {        EngineArray.arrayCopy(getFlat().getWeights(), 0, encoded, 0, getFlat()                .getWeights().length);        int index = getFlat().getWeights().length;        for (RadialBasisFunction rbf : flat.getRBF()) {            encoded[index++] = rbf.getWidth();            EngineArray.arrayCopy(rbf.getCenters(), 0, encoded, index,                    rbf.getCenters().length);            index += rbf.getCenters().length;        }    }

### 2.3 权值的排列

init函数先给各种count赋了值～

//FlatNetwork.java    /**     * Construct a flat network.     *      * @param layers     *            The layers of the network to create.     */    public void init(final FlatLayer[] layers) {        final int layerCount = layers.length;        this.inputCount = layers[0].getCount();        this.outputCount = layers[layerCount - 1].getCount();        this.layerCounts = new int[layerCount];//各层的神经元数        this.layerContextCount = new int[layerCount];//各层的上下文神经元数        this.weightIndex = new int[layerCount];//各层权值在权值数组中的起始index        this.layerIndex = new int[layerCount];//各层神经元的起始index        this.activationFunctions = new ActivationFunction[layerCount];//各层的激活函数        this.layerFeedCounts = new int[layerCount];//从前面一层接收输入的神经元数（偏置神经元和上下文神经元不属于这类）        //下面这两个数组看后面怎么赋值        this.contextTargetOffset = new int[layerCount];//The context target for each layer. This is how the backwards connections are formed for the recurrent neural network. Each layer either has a zero, which means no context target, or a layer number that indicates the target layer.        this.contextTargetSize = new int[layerCount];//The size of each of the context targets. If a layer's contextTargetOffset is zero, its contextTargetSize should also be zero. The contextTargetSize should always match the feed count of the targeted context layer.        this.biasActivation = new double[layerCount];//每层的偏置激励，一般1表示有偏置节点，0表示没有        int index = 0;        int neuronCount = 0;        int weightCount = 0;        //循环顺序是i从大到小，也就是从输出层到输入层        //但是index是从0开始，所以index = 0表示的是输出层，index = layers.length - 1表示的是输入层        for (int i = layers.length - 1; i >= 0; i--) {            final FlatLayer layer = layers[i];            FlatLayer nextLayer = null;            if (i > 0) {                nextLayer = layers[i - 1];            }            this.biasActivation[index] = layer.getBiasActivation();            this.layerCounts[index] = layer.getTotalCount();            this.layerFeedCounts[index] = layer.getCount();            this.layerContextCount[index] = layer.getContextCount();            this.activationFunctions[index] = layer.getActivation();            neuronCount += layer.getTotalCount();            if (nextLayer != null) {                weightCount += layer.getCount() * nextLayer.getTotalCount();            }            if (index == 0) {                this.weightIndex[index] = 0;                this.layerIndex[index] = 0;            } else {                this.weightIndex[index] = this.weightIndex[index - 1]                        + (this.layerCounts[index] * this.layerFeedCounts[index - 1]);                this.layerIndex[index] = this.layerIndex[index - 1]                        + this.layerCounts[index - 1];            }            int neuronIndex = 0;            for (int j = layers.length - 1; j >= 0; j--) {                if (layers[j].getContextFedBy() == layer) {                    this.hasContext = true;                    this.contextTargetSize[index] = layers[j].getContextCount();//表示从index层获得反馈输入的上下文神经元的个数                    this.contextTargetOffset[index] = neuronIndex                            + (layers[j].getTotalCount() - layers[j]                                    .getContextCount());//表示从index层获得反馈输入的上下文神经元的起始标号                    //这两个值全为零表示没有神经元从index层获得反馈输入                }                neuronIndex += layers[j].getTotalCount();            }            index++;        }        this.beginTraining = 0;        this.endTraining = this.layerCounts.length - 1;        this.weights = new double[weightCount];        this.layerOutput = new double[neuronCount];//神经元的输出        this.layerSums = new double[neuronCount];//神经元获得的输入之和，再经过激活函数的作用就可以产生真正的输出layerOutput        clearContext();    }

//FlatNetwork.java    /**     * Calculate a layer.     *      * @param currentLayer     *            The layer to calculate.     */    protected void computeLayer(final int currentLayer) {        final int inputIndex = this.layerIndex[currentLayer];        final int outputIndex = this.layerIndex[currentLayer - 1];        final int inputSize = this.layerCounts[currentLayer];        final int outputSize = this.layerFeedCounts[currentLayer - 1];        int index = this.weightIndex[currentLayer - 1];        final int limitX = outputIndex + outputSize;        final int limitY = inputIndex + inputSize;        // weight values        for (int x = outputIndex; x < limitX; x++) {            double sum = 0;            for (int y = inputIndex; y < limitY; y++) {                sum += this.weights[index++] * this.layerOutput[y];            }            this.layerSums[x] = sum;            this.layerOutput[x] = sum;        }        this.activationFunctions[currentLayer - 1].activationFunction(                this.layerOutput, outputIndex, outputSize);        // update context values        final int offset = this.contextTargetOffset[currentLayer];        EngineArray.arrayCopy(this.layerOutput, outputIndex,                this.layerOutput, offset, this.contextTargetSize[currentLayer]);    }

## 3 RBF激活函数如何作用于网络

//FlatNetworkRBF.java    /**     * Construct an RBF flat network.     *      * @param inputCount     *            The number of input neurons. (also the number of dimensions)     * @param hiddenCount     *            The number of hidden neurons.     * @param outputCount     *            The number of output neurons.     * @param rbf      *              The radial basis functions to use.     */    public FlatNetworkRBF(final int inputCount, final int hiddenCount,            final int outputCount, final RadialBasisFunction[] rbf) {        FlatLayer[] layers = new FlatLayer[3];        this.rbf = rbf;        layers[0] = new FlatLayer(new ActivationLinear(), inputCount, 0.0);        layers[1] = new FlatLayer(new ActivationLinear(), hiddenCount, 0.0);        layers[2] = new FlatLayer(new ActivationLinear(), outputCount, 0.0);        init(layers);    }

The Linear layer is really not an activation function at all. The input is simply passed on, unmodified, to the output.

RBF网络输出的计算是比较特殊的，计算过程在下面的函数中。输入层与隐层之间的连接权值没有意义，全部置零。

//FlatRBFNetwork.java    /**     * Calculate the output for the given input.     *      * @param x     *            The input.     * @param output     *            Output will be placed here.     */    @Override    public void compute(final double[] x, final double[] output) {        int outputIndex = this.getLayerIndex()[1];        //计算隐层节点输出        for (int i = 0; i < rbf.length; i++) {            double o = this.rbf[i].calculate(x);            this.getLayerOutput()[outputIndex + i] = o;        }        // now compute the output        //在计算输出层的输出的时候和BasicNetwork是一样的，于是调用FlatNetwork中的层运算函数        computeLayer(1);        EngineArray.arrayCopy(this.getLayerOutput(), 0, output, 0, this                .getOutputCount());    }

• 私有
• 公开
• 删除