神经网络与RBM

神经网络的前世今生

前言

网络上对波尔兹曼机讨论甚少,所以本文是对RBM的一个简介。

Geoffrey Hinton 被称为"深度学习之父""神经网络先驱",他的主要贡献包括反向传播算法、受限波尔兹曼机、深度置信网络、对比散度算法、ReLU激活单元、Dropout方法,在2017年末又提出胶囊神经网络,改善了CNN。

反向传播

Back Propagation,很难相信它在生物学上成立,并且他需要使用SGD等方法进行优化,这是一个高度非凸的问题,相比之下,SVM显然可解释性更高也更优雅。

  • 收敛速度慢,使用Newton法解决
  • 过拟合,Dropout方法解决
  • 局部极小,使用不同的初始值

限制波尔兹曼机RBM

在第二次NN低谷后,Hinton寻找到了另一个模型:统计热力学模型,根据波尔兹曼统计相关知识,结合马尔科夫随机场等图学习理论,提出波尔兹曼机(BM)。使用能量模型描述神经网络,更加可解释。

人工神经网络

  • 前馈结构(Feed Forward),没有循环,静态的网络
  • 反馈结构(Feedback/ Recurrent),有循环,是动态的网络

HopField网络

它是反馈结构的神经网络,它具有内容寻址记忆的特点,通过与数据部分相似的输入,回想起数据本身。

波尔兹曼机

也是反馈结构,但是他服从波尔兹曼分布,具有隐单元。

RBM

他是波尔兹曼机的限制版本,限制可见层(v,输入)与隐层(h,输出)之间有连接,但是层内无连接,这就不构成循环结构。值得注意的是,上述三个网络的神经元只有0/1状态,即激活与未激活状态。

在统计热力学中,波尔兹曼分布(Gibson分布),用于描述量子体系量子态分布: $P(s) \propto e^{\frac{-E(s)}{kT}}$,其中$s$为量子态,$E(s)$为状态的能量,$P(s)$为这个状态出现的概率,$k$为波尔兹曼常熟,$T$是系统温度(常数),所以可以假设$kT=1$,可以简化为 可以得到每个量子的概率为 这和softmax相同,我们可以再次定义$Z := \sum_{s}e^{-E(s)}$,于是有$P(s) = \frac{1}{Z} e^{-E(s)}$。

为了把这个模型应用在神经网络中,我们需要定义$E$与$s$在神经网络中的角色。前面说过,经典的人工神经网络中,具有可见层隐层(中间层),所以定义$s$为可见层并上隐层的状态,即$s=(v, h)$,$P(v,h) = \frac{1}{Z}e^{-E(v, h)}$。同样巧合的是,物理学中的易辛(Ising)模型,与神经网络极其相似,对于神经元的偏置作为Ising Model的外场,权重$W$作为内部耦合系数(两个神经元之间的权重越大,代表耦合程度越高,关联越强),得到如下能量公式, 可以看出,对于可见层的偏置设为$a$,而对于隐藏层的偏置则为$b$。如果我们将某个神经元$h_{i}$的能量分离出来,也就是 将前半段式子设为$E(v,h’)=-a^{T}v - b’^{T}vh’-h’^{T}W’v$,那么我们就可以得到$P(v, h) = \frac{1}{Z} e^{-E(v, h’)} e^{h_{i}W_{i}v+b_{i}}$,很容易得到 再次得到了Sigmoid函数,也就是 这时候,Sigmoid函数的解释为波尔兹曼分布下隐含层神经元激活的条件概率的激活函数

优化的目标,就是极大似然估计,也就是最大化$P(v)=\frac{1}{Z} \sum_{h}e^{-E(v, h)}$,这个方程与热力统计学中的自由能十分相似$F(v)=-ln\sum_{h}e^{-E(v, h)}$,而式中$Z=\sum_{v}e^{-F(v)}$,于是有$P(v)=\frac{1}{Z}e^{-F(v)}$,而我们的目标是使能量最低的一组参数,在论文A practical guide to training restricted Boltzmann machines Momentum中可以看到更多细节。

不过优化整体网络是很困难的,其根源性在于分配函数$Z$,通常认为这是一个P-Hard问题,如果能够解决,那么很多热力学系统,包括Ising模型也就迎刃而解了。

算法中很重要的手段是贪心,逐层训练网络,而不是整体优化,为了训练每层RBM,Hinton提出了对比散度(Contrastive divergence)算法。利用Gibbs采样,但是收敛速度仍然很慢,所以再次近似固定采样步数$k=1$,这时候效果已经相当好了。这个算法是无监督的学习,不需要标签就可以调优。

不过,后来由于使用ReLU以及合适的初始化,加上CNN,搭配GPU,原来的深度神经网络可以照常训练,不需要使用RBM预训练。在监督学习方面,效果不如直接反向传播,所以最近很少有人再提起,所以如今神经网络模型与30年前(LSTM、CNN)没什么差别,只有trick上的差距。

物理定律是一部分不能用数学证明的真理。

深度置信网络(Deep Belief Networks)

由多个RBM组成,每次训练一个RBM。

Todo List

  • 插入图片

golang template

Golang template 常见问题

————不解决随意可以百度的问题

  1. template 语法
    • 在下文可以用
    • 判断相等用
    • 获取数组长度
    • 判断数组长度为0
  2. template 自定义函数
    • FuncMaps
    • 如果是ParseFiles,则template对象无法使用自定义函数,用如下方法解决
      t, _ := template.New("user.html").Funcs(template.FuncMap{"showTime": func(ts int32) string {
             return time.Unix(int64(ts), 0).Format("2006-01-02 15:04:05")
         }}).ParseFiles("static/views/user.html", "static/views/ref.html")
      
    • 调试——打印error

mongodb + Go

Mongodb

安装

操作

  1. 启动后台服务程序
    mongod  # 如果是默认的数据路径需要root权限, 可以修改数据存储位置。
    
  2. 进入命令行 ```

    mongo

    show databases # 列出数据库 use {database_name} # 切换db指针到某个数据库 show collections # 展示所有的collections (tables) db.{collection_name}.find() # 列出所有元素 .pretty() # 以格式化的json形式展示结果

db.{c_name}.insert({json}) # 插入 db.{c_name}.remove({‘title’: ‘what’}) # 删除指定条件元素 ```

Go 操作Mongodb + 抽象工厂模式设计

import "gopkg.in/mgo.v2"

/** Databaser is An abstract factory interface, if you want to add a new database backend, you must implement all function*/
type Databaser interface {
	iUser
}

func GetDatabaseConncect(db string) (Databaser) {
	if db == "mongodb" { return newMongodb()}
	panic("db is not the validate database backend.")
	return nil
}

// anonymous member, we can directly point the final function, such as AddUser.
type Mongodb struct{
	MongodbUser
}

/** The models definition and corresponding interface. */
type User struct {
	// user'name or oauth2 website username
	Username	string
	// oauth2 authenticated website
	Source 		string
}

type iUser interface {
	AddUser(user User) (error)
	DeleteUser(user User) (error)
	SelectUser(pairs map[string]string) ([]User, error)
}

type MongodbUser struct {
	database 	string
	collection  string	`collection name`
}

// Mongodb's construction, also construct it's interface implementation struct, i.e. subclass.
func newMongodb() (*Mongodb){
	var mdb = new(Mongodb)
	// construct All databases name and collections name, need to be explicit.
	mdb.MongodbUser.database = "Mongodb";
	mdb.MongodbUser.collection = "Users";	// table name.
	return mdb
}

func (self MongodbUser) AddUser(user User) (error) {
	session, err := mgo.Dial(MONGO_DB_SEVER)
	if err != nil {
		return err
	}
	defer session.Close()
	session.SetSafe(&mgo.Safe{})

	c := session.DB(self.database).C(self.collection)
	err = c.Insert(&user)		// It can insert not only one Object, it acquires "...&interface"
	if err != nil {
		return err
	}
	return nil;
}

func (self MongodbUser) DeleteUser(user User) (error) {
	return nil;
}

func (self MongodbUser) SelectUser(pairs map[string]string) ([]User, error) {
	return nil, nil;
}

抽象工厂模式

Word2Vec and Text Classifier

Word2Vec

  1. Skip-gram
    通过目标词汇来预测上下文。
    如:
    我 是 一名 合格 的 共产党员。
    窗口设置为2,生成对(Pair[input, label])为:
    (是, 我), (一名, 我), | (我, 是), (一名, 是), (合格, 是), | (我, 一名), ...
    
  2. CBOW (Continuous bag of words)
    上下文预测目标词汇
    
  3. 使用工具
    pip install word2vec
    
    import word2vec
    word2vec.word2vec('large_pre.dat', 'wordsVec.bin', size=300, verbose=True)
    
    数据格式要求: 空格间隔每个词。
    中文分词工具: jieba (pip)
    

    评价词向量

    ```python model = word2vec.load(“wordsVec.bin”)

    全部的单词

    model.vocab

    第几个单词

    model.vocab[index]

词向量矩阵

model.vectors

获取最近的10(默认)个

indexs = model.cosine(“西瓜”) vocabs = [model.vocab[index] for index in indexs]


4. 其他工具

gensim fasttext glove …


5. 数据来源和预处理

— 搜狗实验室全网新闻数据 http://www.sogou.com/labs/resource/list_news.php — 编码转变和content抽取 由于默认为gbk编码,需要转换成Linux上的utf-8 cat data | iconv -f gbk -t utf-8 -c | grep </content> > utf8data


# FastText --- 文本分类
## 简介

可以训练词向量,可以进行有监督的文本分类,在训练过程中会自动训练词向量,也可以导入预训练的词向量,训练速度极快。 https://github.com/facebookresearch/fastText/archive/v0.1.0.zip


1. 入门

pip install fasttext # 不支持预处理词向量 github下载安装编译,通过CLI运行 数据通过空格分割,结尾是__label___(Yes) # Yes是标签名,前缀可自定义

2. python

import logging import fasttext logging.basicConfig(format=”%(asctime)s : %(levelname)s : %(message)s”, level=logging.INFO)

classifier = fasttext.supervised(“./yes_no_wellformat_train.stat”, “./yes_no_classified.model”, label_prefix=”label”, dim=300, epoch=2, word_ngrams=1, ws=10 )

result = classifier.test(“./yes_no_wellformat_dev.stat”) print(result.precision) print(result.recall) # 召回率 print(result.nexamples) print(classifier.predict_proba([‘对于 散 在 的 比较 小 的 宫颈 腺 囊肿 一般 不需 治疗 , 只要 每年 检查 即可 ; 对于 密集 的 较 小 的 纳氏囊肿 或 比较 大 的 囊肿 , 可 考虑 光疗 、 激光 、 微波 、 自凝 刀 等 物>理治疗 ; 对于 较 大 的 突出 于 宫颈 表面 的 , 可 考虑 电刀 切除 治疗 。’])) # 需要空格分开


3. CLI

./fasttext supervised -input yes_no_wellformat_train.stat -output model -dim 300 -pretrainedVectors vocab_emb.dict -epoch 2 -loss ns


4. 效果
* epoch 不宜太大
* 预处理词向量意义不大,具体应该和规模有关
* dim大一些有好处

5. Tips
* 预处理的词向量格式

单词数 维度 单词 数字(用空格分割)

可以通过减少精度来降低词向量占用的磁盘空间 ```

RNN Primer

RNN

  • Reference (https://zhuanlan.zhihu.com/p/28054589)

网络

  1. 输入x1, x2, x3, x4 …
  2. 计算隐藏层参数:
    h1 = f(U*x1 + W*h0 + b)
    h2 = f(U*x2 + W*h1 + b)
    h3 = f(U*x3 + W*h2 + b)
    h4 = f(U*x4 + W*h3 + b)
    ... 
    
  3. 输出
    y1 = Softmax(V*h1 + c)
    y2 = Softmax(V*h2 + c)
    y3 = Softmax(V*h3 + c)
    y4 = Softmax(V*h4 + c)
    ...
    
    • 注: 输入与输出等长度。所以经典RNN应用范围较小,如视频抽帧,预测下一个词语等输入序列与输出序列相同可以使用。
  4. 单输出值
    Y = Softmax(V*h4 + c)
    
    • 应用:输入一段文字判断类别,情感分析(倾向)。
  5. 单输入多输出
    h1 = f(U*x + W*h0 + b)
    h2 = f(U*x + W*h1 + b)
    h3 = f(U*x + W*h2 + b)
    h4 = f(U*x + W*h3 + b)
    ... 
    
    • 应用:输入一个类别,生成一段话。

多输入多输出(Encoder-Decoder)/(Seq2Seq)

  1. 构建隐层网络,得到最后一个隐层值(h4)
  2. 得到c, 多种方式
    • c = h4
    • c = q(h4)
    • c = q(h1, h2, h3, h4)
  3. 把c作为单输入,输入到另外一个神经网络中。
    • 应用:机器翻译,阅读理解,语音识别。

注意力(Attention)机制

Tensorflow Knowledge

Tensorflow note

基本用法

  • 起始节点
    c = tf.Constant(tf.int32, shape=[None, 1], name="Constant")
    v = tf.Variable(...)    # 运行中被更新
    p = tf.placeholder(...)
    
  • shape
    [None, 784]   # None代表第一纬度不确定,视输入而定, 用于不定batch输入
    tf.reshape(x, [-1, 28, 28, 1])  # 第一个纬度根据计算得到。
    
  • operation
    tf.matmul() #矩阵乘法
    tf.square()
    tf.log()
    tf.reduce_sum()
    tf.arg_max()    
    tf.equal()
    tf.assign()
    
  • 初始化 1、 简单初始化
    # tf.initialize_all_variables() 已经被废弃
    init = tf.global_variables_initializer()    # 在session中,可以直接init.run()
    sess.run(init)
    

    2、 复杂初始化

    # 截断正态分布
    tf.Variable(tf.truncated_normal(shape, stddev=0.1))
    tf.randint(-1, 1)
    tf.uniform_random()
    
  • 损失
    # 交叉损失熵
    # softmax is used to normalize all dimesion.
    y = tf.nn.softmax(tf.matmul(x, W) + b)
    cross_entropy = -tf.reduce_sum(y_ * tf.log(y))  # y_hat(label) multiply log(y)
    
  • 优化
    optimizer = tf.train.GradientDescentOptimizer(0.01)
    train = optimizer.minimize(loss)
    _, loss = sess.run([train, loss], feed_dict={x: xx})
    print(loss)     # 运行中查看损失    
    
  • 评估模型 ```python tf.argmax(y, 1) # 在第二维度找最大值变为1, 如softmax后,每个纬度都有取值,找到最大值变为1, 第一维度用于batch

训练结果与标签值每个判断是否相等,得到的correct的形式类似于

[True, True, False, True]

correct = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))

转换为准确率

cast用于把True变为1, False变为0

reduce_mean取平均值

accuracy = tf.reduce_mean(tf.cast(correct, “float”))

运行accuracy 的Graph

a = sess.run(accuracy, feed_dict={test_dict})


* Session 和 Graph

sess = tf.InteractiveSession() 直接成为默认的Session, 可以直接用op.run() 或 tensor.eval() 结束时必须使用 sess.close()

1、 正常情况下
```python
with tf.Session() as sess:
    sess.run(...)

2、 默认session

sess = tf.Session()
with sess.as_default():
    c.eval()
# 尽管调用完毕,但sess仍是默认Session(),必须显示调用sess.close()

保存和加载变量

saver = tf.train.Saver()
saver.save(self.sess, "./model.ckpt", global_step=1)    # 目录./是很必要的,或者用os.path.join(...)

# 加载
self.saver.restore(self.sess, "./model.ckpt-1")     # -1 是保存中指定的global_step
  • 注:变量的name不能改变,在保存和加载的过程中。

模型

  • CNN ```python

    conv2d代表二维卷积网络(如图片),需要输入四个纬度的元素, 第一纬度为batch_size(无意义), 第二、三分别维宽和高,第四个纬度为每个元素的通道数,如黑白为1, 彩色为3.

    stride为步长, 重点关注第二第三纬度。

    padding为SAME: 输出与输入的形状(shape)相同

    padding为VALID: 输出与输入通过公式计算: width= (28-5+2*0/1)+1=24,

    卷积层

    tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding=”SAME”)

ksize代表卷积核的大小,本例为2*2,步长分别为2, 2

输出的大小为width/=2, height/=2

池化层

tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding=”SAME”)

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) h_pool1 = max_pool(h_conv1)

W_conv2 = …


## tensorboard
```python
# 设置图标中记录的名称和变量值。
tf.summary.scalar('loss', cross_entropy)
# merge所有的操作符
merged_summary_op = tf.summary.merge_all()
# 定义写入目录。
summary_writer = tf.summary.FileWriter("/tmp/mnist_logs", sess.graph)
# 运行merge操作符并写入。
summary_str = sess.run(merged_summary_op, feed_dict={x: batch_xs, y_: batch_ys})
summary_writer.add_summary(summary_str, i)

启动

tensorboard --logdir /tmp/mnist_logs

scope(variable/name)

  • variable_scope It is used to share variable by get_variable. ```python with tf.variable_scope(‘v_scope’) as scope1: Weights1 = tf.get_variable(‘Weights’, shape=[2,3])

下面来共享上面已经定义好的变量

with tf.variable_scope(‘v_scope’, reuse=True) as scope2: Weights2 = tf.get_variable(‘Weights’)

- name_scope
It is mainly used to create diffent variable with same name. `It will create a new name_scope, if the name has been existed.`e.g. `conv1_1`.
Usually every layer is a name_scope.
```python
with tf.name_scope('conv1') as scope:
    weights1 = tf.Variable([1.0, 2.0], name='weights')
    bias1 = tf.Variable([0.3], name='bias')

with tf.name_scope('conv2') as scope:
    weights2 = tf.Variable([4.0, 2.0], name='weights')
    bias2 = tf.Variable([0.33], name='bias')