diff --git a/src/main/java/org/dromara/easyai/nerveCenter/NerveManager.java b/src/main/java/org/dromara/easyai/nerveCenter/NerveManager.java new file mode 100644 index 0000000..3c1eb4e --- /dev/null +++ b/src/main/java/org/dromara/easyai/nerveCenter/NerveManager.java @@ -0,0 +1,464 @@ +package org.dromara.easyai.nerveCenter; + +import org.dromara.easyai.conv.ConvCount; +import org.dromara.easyai.i.CustomEncoding; +import org.dromara.easyai.matrixTools.Matrix; +import org.dromara.easyai.i.ActiveFunction; +import org.dromara.easyai.nerveEntity.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 神经网络管理工具 + * 创建神经网络 + * + * @author lidapeng + * @date 11:05 上午 2019/12/21 + */ +public class NerveManager extends ConvCount { + private final int hiddenNerveNub;//隐层神经元个数 + private final int sensoryNerveNub;//输入神经元个数 + private final int outNerveNub;//输出神经元个数 + private final int hiddenDepth;//隐层深度 + private final List sensoryNerves = new ArrayList<>();//感知神经元 + private SensoryNerve convInput;//卷积网络输入神经元 + private final List> depthNerves = new ArrayList<>();//隐层神经元 + private List convDepthNerves = new ArrayList<>();//卷积隐层神经元 + private final List outNerves = new ArrayList<>();//输出神经元 + private final List softMaxList = new ArrayList<>();//softMax层 + private boolean initPower; + private float studyPoint = 0.001f;//学习率 + private float convStudyPoint = 0.001f;//卷积学习率 + private float oneConvRate = 0.001f; + private final ActiveFunction activeFunction; + private final int rzType;//正则化类型,默认不进行正则化 + private final float lParam;//正则参数 + private final int coreNumber; + private final float gaMa;//自适应学习率 + private final float gMaxTh;//梯度裁剪阈值 + private final boolean auto; + + public SensoryNerve getConvInput() { + return convInput; + } + + private Map conversion(Map map) { + Map cMap = new HashMap<>(); + for (Map.Entry entry : map.entrySet()) { + cMap.put(String.valueOf(entry.getKey()), entry.getValue()); + } + return cMap; + } + + private Map unConversion(Map map) { + Map cMap = new HashMap<>(); + for (Map.Entry entry : map.entrySet()) { + cMap.put(Integer.parseInt(entry.getKey()), entry.getValue()); + } + return cMap; + } + + private ModelParameter getDymModelParameter() throws Exception {//获取动态神经元参数 + ModelParameter modelParameter = new ModelParameter(); + List convStudies = new ArrayList<>(); + modelParameter.setDymNerveStudies(convStudies); + for (Nerve convDepthNerve : convDepthNerves) { + ConvParameter convParameter = convDepthNerve.getConvParameter(); + List nerveMatrixList = convParameter.getNerveMatrixList();//权重矩阵 + ConvDymNerveStudy convDymNerveStudy = new ConvDymNerveStudy(); + List> oneConvList = convParameter.getOneConvPower(); + List dymNerveStudies = new ArrayList<>();//一个卷积层的所有权重参数 + convDymNerveStudy.setOneConvPower(oneConvList); + convDymNerveStudy.setDymNerveStudyList(dymNerveStudies); + for (Matrix nerveMatrix : nerveMatrixList) { + DymNerveStudy deepNerveStudy = new DymNerveStudy();//动态神经元隐层 + List list = deepNerveStudy.getList(); + insertWList(nerveMatrix, list); + dymNerveStudies.add(deepNerveStudy); + } + convStudies.add(convDymNerveStudy); + } + getStaticModelParameter(modelParameter); + return modelParameter; + } + + private void insertWList(Matrix matrix, List list) throws Exception {// + for (int i = 0; i < matrix.getX(); i++) { + for (int j = 0; j < matrix.getY(); j++) { + list.add(matrix.getNumber(i, j)); + } + } + } + + public ModelParameter getConvModel() throws Exception { + return getDymModelParameter(); + } + + public ModelParameter getDnnModel() throws Exception { + ModelParameter modelParameter = new ModelParameter(); + getStaticModelParameter(modelParameter); + return modelParameter; + } + + private void getStaticModelParameter(ModelParameter modelParameter) {//获取当前模型参数 + List> studyDepthNerves = new ArrayList<>();//隐层神经元模型 + List outStudyNerves = new ArrayList<>();//输出神经元 + //隐层神经元 + for (List depthNerve : depthNerves) { + //创建一层深度的隐层神经元模型 + List deepNerve = new ArrayList<>(); + for (Nerve nerve : depthNerve) { + //遍历某一层深度的所有隐层神经元 + NerveStudy nerveStudy = new NerveStudy(); + nerveStudy.setThreshold(nerve.getThreshold()); + nerveStudy.setDendrites(conversion(nerve.getDendrites())); + deepNerve.add(nerveStudy); + } + studyDepthNerves.add(deepNerve); + } + for (Nerve nerve : outNerves) { + NerveStudy nerveStudy = new NerveStudy(); + nerveStudy.setThreshold(nerve.getThreshold()); + nerveStudy.setDendrites(conversion(nerve.getDendrites())); + outStudyNerves.add(nerveStudy); + } + modelParameter.setDepthNerves(studyDepthNerves); + modelParameter.setOutNerves(outStudyNerves); + } + + public void insertConvModel(ModelParameter modelParameter) throws Exception { + insertConvolutionModelParameter(modelParameter);//动态神经元注入 + } + + public void insertDnnModel(ModelParameter modelParameter) { + insertBpModelParameter(modelParameter);//全连接层注入参数 + } + + //注入卷积层模型参数 + private void insertConvolutionModelParameter(ModelParameter modelParameter) throws Exception { + List allDymNerveStudyList = modelParameter.getDymNerveStudies(); + for (int t = 0; t < allDymNerveStudyList.size(); t++) { + ConvParameter convParameter = convDepthNerves.get(t).getConvParameter(); + List nerveMatrixList = convParameter.getNerveMatrixList(); + ConvDymNerveStudy convDymNerveStudy = allDymNerveStudyList.get(t); + List> oneConvPower = convDymNerveStudy.getOneConvPower(); + if (oneConvPower != null && !oneConvPower.isEmpty()) { + convParameter.setOneConvPower(oneConvPower); + } + List dymNerveStudyList = convDymNerveStudy.getDymNerveStudyList(); + if (dymNerveStudyList.size() != nerveMatrixList.size()) { + throw new Exception("卷积层数量参数与模型不匹配"); + } + for (int i = 0; i < dymNerveStudyList.size(); i++) { + List list = dymNerveStudyList.get(i).getList(); + Matrix nerveMatrix = nerveMatrixList.get(i); + insertMatrix(nerveMatrix, list); + } + } + insertBpModelParameter(modelParameter);//全连接层注入参数 + } + + private void insertMatrix(Matrix matrix, List list) throws Exception { + for (int i = 0; i < list.size(); i++) { + matrix.setNub(i, 0, list.get(i)); + } + } + + //注入全连接模型参数 + private void insertBpModelParameter(ModelParameter modelParameter) { + List> depthStudyNerves = modelParameter.getDepthNerves();//隐层神经元 + List outStudyNerves = modelParameter.getOutNerves();//输出神经元 + //隐层神经元参数注入 + for (int i = 0; i < depthNerves.size(); i++) { + List depth = depthStudyNerves.get(i);//对应的学习结果 + List depthNerve = depthNerves.get(i);//深度隐层神经元 + for (int j = 0; j < depthNerve.size(); j++) {//遍历当前深度神经元 + Nerve nerve = depthNerve.get(j); + NerveStudy nerveStudy = depth.get(j); + //学习结果 + Map studyDendrites = unConversion(nerveStudy.getDendrites()); + //神经元参数注入 + Map dendrites = nerve.getDendrites(); + nerve.setThreshold(nerveStudy.getThreshold());//注入隐层阈值 + for (Map.Entry entry : dendrites.entrySet()) { + int key = entry.getKey(); + dendrites.put(key, studyDendrites.get(key));//注入隐层权重 + } + } + } + //输出神经元参数注入 + for (int i = 0; i < outNerves.size(); i++) { + Nerve outNerve = outNerves.get(i); + NerveStudy nerveStudy = outStudyNerves.get(i); + outNerve.setThreshold(nerveStudy.getThreshold()); + Map dendrites = outNerve.getDendrites(); + Map studyDendrites = unConversion(nerveStudy.getDendrites()); + for (Map.Entry outEntry : dendrites.entrySet()) { + int key = outEntry.getKey(); + dendrites.put(key, studyDendrites.get(key)); + } + } + } + + /** + * 初始化神经元参数 + * + * @param sensoryNerveNub 输入神经元个数 + * @param hiddenNerveNub 隐层神经元个数 + * @param outNerveNub 输出神经元个数 + * @param hiddenDepth 隐层深度 + * @param activeFunction 激活函数 + * @param studyPoint 线性分类器学习率 + * @param rzType 正则函数 + * @param lParam 正则系数 + * @param coreNumber 并行计算核心数 + * @param gaMa 自适应学习率衰减系数 + * @param gMaxTh 梯度裁剪阈值 + * @param auTo 是否使用自适应学习率 + * @throws Exception 如果参数错误则抛异常 + */ + public NerveManager(int sensoryNerveNub, int hiddenNerveNub, int outNerveNub + , int hiddenDepth, ActiveFunction activeFunction, float studyPoint, int rzType, float lParam + , int coreNumber, float gaMa, float gMaxTh, boolean auTo) throws Exception { + if (sensoryNerveNub > 0 && hiddenNerveNub > 0 && outNerveNub > 0 && hiddenDepth > 0 && activeFunction != null) { + this.coreNumber = coreNumber; + this.gaMa = gaMa; + this.auto = auTo; + this.gMaxTh = gMaxTh; + this.hiddenNerveNub = hiddenNerveNub; + this.sensoryNerveNub = sensoryNerveNub; + this.outNerveNub = outNerveNub; + this.hiddenDepth = hiddenDepth; + this.activeFunction = activeFunction; + this.rzType = rzType; + this.lParam = lParam; + if (studyPoint > 0 && studyPoint < 1) { + this.studyPoint = studyPoint; + } + } else { + throw new Exception("param is null"); + } + } + + public List getSensoryNerves() {//获取感知神经元集合 + return sensoryNerves; + } + + private List initConDepthNerve(int kernLen, int conHiddenDepth, ActiveFunction convFunction, int channelNo, boolean norm, float GRate) throws Exception {//初始化隐层神经元1 + List depthNerves = new ArrayList<>(); + for (int i = 0; i < conHiddenDepth; i++) {//遍历深度 + float studyPoint = this.convStudyPoint; + if (studyPoint <= 0 || studyPoint > 1) { + throw new Exception("studyPoint Values range from 0 to 1"); + } + int downNub = 1; + boolean isConvFinish = false; + if (i == conHiddenDepth - 1) {//卷积层最后一层 + downNub = hiddenNerveNub; + isConvFinish = true; + } + HiddenNerve hiddenNerve = new HiddenNerve(1, i + 1, 1, downNub, studyPoint, initPower, convFunction, true + , rzType, lParam, kernLen, 0, 0, isConvFinish, coreNumber, channelNo, oneConvRate, norm, + null, gaMa, gMaxTh, auto, GRate); + depthNerves.add(hiddenNerve); + } + for (int i = 0; i < conHiddenDepth - 1; i++) {//遍历深度 + Nerve hiddenNerve = depthNerves.get(i);//当前遍历隐层神经元 + Nerve nextHiddenNerve = depthNerves.get(i + 1);//当前遍历的下一层神经元 + hiddenNerve.connectSonOnly(nextHiddenNerve); + nextHiddenNerve.connectFatherOnly(hiddenNerve); + } + return depthNerves; + } + + + private int getNerveNub(int deep, int size, int kernLen) { + int x = size; + int step = 1; + for (int i = 0; i < deep; i++) { + x = (x - (kernLen - step)) / step; + x = x / 2 + x % 2; + } + return x; + } + + /** + * 初始化卷积层神经网络 + * + * @param channelNo 通道数,当该数值为1 则采用多通道降维模式 推荐值1 + * @param kernLen 卷积核大小 建议为3 + * @param xSize 检测窗口行高 + * @param ySize 检测窗口行宽 + * @param convStudyPoint 卷积层学习率 + * @param convFunction 卷积层激活函数 + * @param isShowLog 是否打印学习参数 + * @param isSoftMax 最后一层是否用softMax激活 + * @param minFeatureValue 卷积层最小特征数量的开方 取值范围 [1,50] + * @param norm 是否进行维度调节,true 进行调节, false不进行维度调节 + * @param oneConvRate 降维层学习率 + * @param GRate 每层的梯度衰减阈值 + */ + public void initImageNet(int channelNo, int kernLen, int xSize, int ySize, boolean isSoftMax, boolean isShowLog, + float convStudyPoint, ActiveFunction convFunction, int minFeatureValue, float oneConvRate + , boolean norm, float GRate) throws Exception { + this.initPower = true; + this.oneConvRate = oneConvRate; + if (minFeatureValue < 1 || minFeatureValue > 50) { + throw new Exception("minFeatureValue 取值范围是[1,50]"); + } + if (channelNo < 1) { + throw new Exception("通道数不能小于1"); + } + if (!norm) {//如果不进行维度调节,通道数必须为3 + channelNo = 3; + } + this.convStudyPoint = convStudyPoint; + int deep = getConvMyDep(xSize, ySize, kernLen, minFeatureValue);//卷积层深度 + if (deep < 2) { + throw new Exception("minFeatureValue 设置过大"); + } + List myDepthNerves = initConDepthNerve(kernLen, deep, convFunction, channelNo, norm, GRate);//初始化卷积层隐层 + Nerve convFirstNerve = myDepthNerves.get(0);//卷积第一层隐层神经元 + Nerve convLastNerve = myDepthNerves.get(myDepthNerves.size() - 1);//卷积最后一层隐层神经元 + convDepthNerves = myDepthNerves; + convInput = new SensoryNerve(1, 0, channelNo);//输入神经元 + //感知神经元与卷积第一层隐层神经元进行连接 + convInput.connectSonOnly(convFirstNerve); + initDepthNerve(kernLen, getNerveNub(deep, xSize, kernLen), getNerveNub(deep, ySize, kernLen), channelNo, null);//初始化深度隐层神经元 depthNerves + List firstNerves = depthNerves.get(0);//线性层第一层隐层神经元 + List lastNerveList = depthNerves.get(depthNerves.size() - 1);//线性层最后一层隐层神经元 + convLastNerve.connect(firstNerves);//卷积最后一层链接线性层第一层 + for (Nerve nerve : firstNerves) {//线性层第一层链接卷积层最后一层 + nerve.connectFatherOnly(convLastNerve); + } + List myOutNerveList = new ArrayList<>(); + //初始化输出神经元 + for (int i = 1; i < outNerveNub + 1; i++) { + OutNerve outNerve = new OutNerve(i, hiddenNerveNub, 0, studyPoint, initPower, + activeFunction, false, isShowLog, rzType, lParam, isSoftMax, 0 + , coreNumber, gaMa, gMaxTh, auto, 1); + //输出层神经元连接最后一层隐层神经元 + outNerve.connectFather(lastNerveList); + outNerves.add(outNerve); + myOutNerveList.add(outNerve); + } + //生成softMax层 + if (isSoftMax) {//增加softMax层 + SoftMax softMax = new SoftMax(outNerveNub, false, myOutNerveList, isShowLog, coreNumber); + softMaxList.add(softMax); + for (Nerve nerve : outNerves) { + nerve.connect(softMaxList); + } + } + //最后一层隐层神经元 与输出神经元进行连接 + for (Nerve nerve : lastNerveList) { + nerve.connect(outNerves); + } + + } + + /** + * 初始化 + * + * @param initPower 是否是第一次注入 + * @param isShowLog 是否打印学习参数 + * @param isSoftMax 最后一层是否用softMax激活 + */ + public void init(boolean initPower, boolean isShowLog, boolean isSoftMax, CustomEncoding customEncoding) throws Exception {//进行神经网络的初始化构建 + this.initPower = initPower; + initDepthNerve(0, 0, 0, 0, customEncoding);//初始化深度隐层神经元 + List nerveList = depthNerves.get(0);//第一层隐层神经元 + //最后一层隐层神经元啊 + List lastNerveList = depthNerves.get(depthNerves.size() - 1); + List myOutNerveList = new ArrayList<>(); + //初始化输出神经元 + for (int i = 1; i < outNerveNub + 1; i++) { + OutNerve outNerve = new OutNerve(i, hiddenNerveNub, 0, studyPoint, initPower, + activeFunction, false, isShowLog, rzType, lParam, isSoftMax, 0 + , coreNumber, gaMa, gMaxTh, auto, 1); + //输出层神经元连接最后一层隐层神经元 + outNerve.connectFather(lastNerveList); + outNerves.add(outNerve); + myOutNerveList.add(outNerve); + } + //生成softMax层 + if (isSoftMax) {//增加softMax层 + SoftMax softMax = new SoftMax(outNerveNub, false, myOutNerveList, isShowLog, coreNumber); + softMaxList.add(softMax); + for (Nerve nerve : outNerves) { + nerve.connect(softMaxList); + } + } + //最后一层隐层神经元 与输出神经元进行连接 + for (Nerve nerve : lastNerveList) { + nerve.connect(outNerves); + } + + //初始化感知神经元 + for (int i = 1; i < sensoryNerveNub + 1; i++) { + SensoryNerve sensoryNerve = new SensoryNerve(i, 0, 0); + //感知神经元与第一层隐层神经元进行连接 + sensoryNerve.connect(nerveList); + sensoryNerves.add(sensoryNerve); + } + + } + + private void initDepthNerve(int kernLen, int matrixX, int matrixY, int channelNo, CustomEncoding customEncoding) throws Exception {//初始化隐层神经元1 + for (int i = 0; i < hiddenDepth; i++) {//遍历深度 + List hiddenNerveList = new ArrayList<>(); + float studyPoint = this.studyPoint; + if (studyPoint <= 0 || studyPoint > 1) { + throw new Exception("studyPoint Values range from 0 to 1"); + } + CustomEncoding myCustomEncoding = null; + if (i == 0) { + myCustomEncoding = customEncoding; + } + for (int j = 1; j < hiddenNerveNub + 1; j++) {//遍历同级 + int upNub; + int downNub; + int myMatrixX = 0; + int myMatrixY = 0; + if (i == 0) { + myMatrixX = matrixX; + myMatrixY = matrixY; + if (matrixX > 0 && matrixY > 0) { + upNub = matrixX * matrixY * channelNo; + } else { + upNub = sensoryNerveNub; + } + } else { + upNub = hiddenNerveNub; + } + if (i == hiddenDepth - 1) {//最后一层隐层神经元z + downNub = outNerveNub; + } else { + downNub = hiddenNerveNub; + } + HiddenNerve hiddenNerve = new HiddenNerve(j, i + 1, upNub, downNub, studyPoint, initPower, activeFunction, false + , rzType, lParam, kernLen, myMatrixX, myMatrixY, false, coreNumber, 0, oneConvRate, false + , myCustomEncoding, gaMa, gMaxTh, auto, 1); + hiddenNerveList.add(hiddenNerve); + } + depthNerves.add(hiddenNerveList); + } + initHiddenNerve(); + } + + private void initHiddenNerve() {//初始化隐层神经元2 + for (int i = 0; i < hiddenDepth - 1; i++) {//遍历深度 + List hiddenNerveList = depthNerves.get(i);//当前遍历隐层神经元 + List nextHiddenNerveList = depthNerves.get(i + 1);//当前遍历的下一层神经元 + for (Nerve hiddenNerve : hiddenNerveList) { + hiddenNerve.connect(nextHiddenNerveList); + } + for (Nerve nextHiddenNerve : nextHiddenNerveList) { + nextHiddenNerve.connectFather(hiddenNerveList); + } + } + } +}