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); } } } }