如何利用 C# 抽象神经网络模型?

旅游需求预测在旅游业发展中具有重要的作用。正确的预测是进行科学决策的依据。制定发展战略、编制计划以及日常管理的决策,都是以科学的预测工作为前提的。

常见的旅游需求预测方法是基于统计学的数学模型:时间序列预测法和因果模型预测法。然而,旅游市场往往受到许多因素的制约,这些因素之间呈现出错综复杂的关系,而且不稳定因素也很多,市场多变,难以得到有效的预测结果。有必要研究应用一些新的解决非线性问题的方法,如神经网络、灰色模型等。

以上是《基于BP神经网络的云南国际旅游需求预测》中引言部分的一小段,该论文利用BP神经网络对云南旅游外汇收入及入境游客人数进行预测和分析,分析结果表明,人工神经网络方法在旅游预测中的应用是可行的,而且是十分有效的。公众号后台回复 20190316 可以下载这篇论文学习。

由于神经网络根据其结构和功能的不同,可以构成不同的网络,处理不同的问题(分类、回归、预测、评价、聚类等等)。所以,我们在写神经网络的代码时,就要考虑遵循一些程序设计的原则,在这些原则的指导下,把代码写的可复用、可扩展、易于维护、灵活性好。

今天,我们就先构造神经网络的抽象层部分,后面我们再写一些图文来构造具体的实现部分(包括感知器、线性神经网络、BP神经网络、进化神经网络、SOM神经网络、基于概率模型的神经网络等),根据不同的实现可以得到不同类型的神经网络。


神经网络(Neural network)

「神经网络」是一种由许多简单的并行工作的处理单元组成的系统,其功能取决于网络的结构,连接强度以及各单元的处理方式。学习神经网络要搞清楚这些基本概念,比如:神经元模型、激活函数、权值阈值、学习算法等,有关于神经网络的详细介绍可以参见维基百科中的相应部分。

https://en.wikipedia.org/wiki/Neural_network

神经网络

单一职责原则(Single responsibility principle)

「单一职责原则」是指一个类只负责一个功能领域中的相应职责。即就一个类而言,应该只有一个引起它变化的原因。有关该原则的详细介绍可以参见维基百科中的相应部分。

https://en.wikipedia.org/wiki/Single_responsibility_principle

单一职责原则

里氏代换原则(Liskov substitution principle)

「里氏代换原则」是指所有引用基类的地方必须能透明地使用其子类的对象。有关该原则的详细介绍可以参见维基百科中的相应部分。

https://en.wikipedia.org/wiki/Liskov_substitution_principle

里氏代换原则

依赖倒置原则

「依赖倒置原则」是指抽象不应该依赖于细节,细节应该依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。有关该原则的详细介绍可以参见维基百科中的相应部分。

https://en.wikipedia.org/wiki/Dependency_inversion_principle

依赖倒转原则

通过以上的介绍,我们了解了神经网络的基本概念,根据 单一职责原则,我们需要设计不同的类去承担不同的职责,根据 里氏代换原则依赖倒置原则,我们可以先定义不容易发生改变的抽象类和接口,等后期我们再根据使用场景选择不同的实体类来继承或实现它们,通过 运行时多态,在程序运行的时候动态的选择运行哪个类中的哪个方法,来达到我们的目标。基本思路就是这样,下面我们来看一下对神经网络进行抽象的代码。

1. 对神经元的抽象,封装了诸如神经元的输入、输出和权值等常见的属性。

public abstract class Neuron
{
    // 随机数生成器
    public static Random Rand { get; set; } = new Random();
    
    // 随机数范围
    public static Range RandRange { get; set; } = new Range(0.0f, 1.0f); 
    
    // 多输入
    public int InputsCount { get; }
    
    // 单输出
    public double Output { get; protected set; } = 0.0;  
    
    // 权值数组
    public double[] Weights { get; }

    // 构造函数
    protected Neuron(int inputs)
    {
        InputsCount = Math.Max(1, inputs);
        Weights = new double[InputsCount];
        Randomize();
    }
    
    // 初始化权值
    public virtual void Randomize()
    {
        double d = RandRange.Length;
        for (int i = 0; i < InputsCount; i++)
            Weights[i] = Rand.NextDouble() * d + RandRange.Min;
    }
    // 输出
    public abstract double Compute(double[] input);
}    

2. 对激活函数的抽象。

所有与神经元一起使用的激活函数,都应该实现这个接口,这些函数将神经元的输出作为其输入加权和的函数来计算。

public interface IActivationFunction
{
    // 激活函数
    double Function(double x);
    
    // 求x点的导数
    double Derivative(double x);
    
    // 求y点的导数
    double Derivative2(double y);
}

3. 对神经网络层的抽象,代表该层神经元的集合。

public abstract class Layer
{
    // 该层神经元的个数
    protected int NeuronsCount;
    
    // 该层的输入个数
    public int InputsCount { get; }
    
    // 该层神经元的集合
    public Neuron[] Neurons { get; }
    
    // 该层的输出向量
    public double[] Output { get; protected set; }
    
    // 构造函数
    protected Layer(int neuronsCount, int inputsCount)
    {
        InputsCount = Math.Max(1, inputsCount);
        NeuronsCount = Math.Max(1, neuronsCount);
        Neurons = new Neuron[NeuronsCount];
    }
    
    // 计算该层的输出向量
    public virtual double[] Compute(double[] input)
    {
        double[] output = new double[NeuronsCount];
        for (int i = 0; i < Neurons.Length; i++)
            output[i] = Neurons[i].Compute(input);

        Output = output;
        return output;
    }
    
    // 初始化该层神经元的权值
    public virtual void Randomize()
    {
        foreach (Neuron neuron in Neurons)
            neuron.Randomize();
    }
}

4. 对学习算法的抽象。

由于机器学习可以分为带标签的监督学习和不带标签的非监督学习,所以这块的抽象也分成两种:第一种是对监督学习的抽象,第二种是对非监督学习的抽象。

对监督学习的抽象,这个接口描述了所有监督学习算法应该实现的方法。监督学习就是这样的一种学习算法,在学习阶段系统的期望输出是已知的。

public interface ISupervisedLearning
{
    // 单样本训练
    double Run(double[] input, double[] output);
    
    // 多样本训练
    double RunEpoch(double[][] input, double[][] output);
}

对非监督学习的抽象,该接口描述了所有非监督学习算法都应该实现的方法。非监督学习就是这种类型的学习算法,在学习阶段系统的期望输出是未知的。

public interface IUnsupervisedLearning
{
    // 单样本训练
    double Run(double[] input);
    
    // 多样本训练
    double RunEpoch(double[][] input);
}

5. 对神经网络结构的抽象,它表示神经元层的集合。

public abstract class Network
{
    // 网络层的个数
    protected int LayersCount;

    // 网络输入的个数
    public int InputsCount { get; }

    // 构成网络的层
    public Layer[] Layers { get; }

    // 网络的输出向量
    public double[] Output { get; protected set; }


    // 构造函数 
    protected Network(int inputsCount, int layersCount)
    {
        InputsCount = Math.Max(1, inputsCount);
        LayersCount = Math.Max(1, layersCount);
        Layers = new Layer[LayersCount];
    }

    // 计算网络的输出
    public virtual double[] Compute(double[] input)
    {
        double[] output = input;

        for (int i = 0; i < Layers.Length; i++)
        {
            output = Layers[i].Compute(output);
        }
        Output = output;
        return output;
    }

    // 初始化整个网络的权值
    public virtual void Randomize()
    {
        foreach (Layer layer in Layers)
        {
            layer.Randomize();
        }
    }

    // 保存网络结构
    public void Save(string fileName)
    {
        FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
        Save(stream);
        stream.Close();
    }

    // 加载网络结构
    public static Network Load(string fileName)
    {
        FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
        Network network = Load(stream);
        stream.Close();
        return network;
    }
}

通过上面的介绍,神经网络模型已经被我们抽象出来,由于没有写具体的实现,暂时还不能使用。不要紧,后面我们会写一系列的实体类去继承或实现这些抽象类或接口以便得到不同类型的神经网络。

今天就到这里吧!希望大家能够有所收获!See You!


相关图文

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 143,540评论 1 302
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 61,539评论 1 258
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 95,005评论 0 213
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 41,159评论 0 180
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 48,946评论 1 258
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 38,864评论 1 178
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,468评论 2 273
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,218评论 0 167
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 29,070评论 6 234
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 32,574评论 0 213
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,353评论 2 215
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 30,683评论 1 232
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 24,270评论 0 32
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,168评论 2 214
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 31,591评论 3 210
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,638评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,035评论 0 166
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 33,590评论 2 232
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 33,685评论 2 233

推荐阅读更多精彩内容

  • 原文地址:http://www.cnblogs.com/subconscious/p/5058741.html 神...
    Albert陈凯阅读 5,366评论 0 48
  • 主要内容 自然语言输入编码 前馈网络 卷积网络 循环网络(recurrent networks ) 递归网络(re...
    JackHorse阅读 3,955评论 0 2
  • 目标,是一个人前进的动力,是一个人变得优秀的重要因素,因为目标,人才会知道什么时候改做什么,怎么让自己逼近目...
    lyang阅读 337评论 0 0
  • 男神加缪,因车祸骤逝,距今已有58个年头。他短短47载生命,一顶哲学桂冠,一个诺贝尔文学奖,关键是,还挺帅。 然而...
    子迪君阅读 264评论 0 4
  • 那天听我在深圳上班的朋友说,她的上司每天工作到凌晨1点,早上6-30起来坚持看半个小时书然后上班,把每天时间...
    奶茶有点甜么阅读 143评论 0 2