一.概述
CapsuleNet(Dynamic Routing Between Capsules),胶囊网络,是Hinton等提出的新型图像人工神经网络模型,它主要解决了图像领域中maxpooling等信息丢失(比如说相对位置信息丢失的问题等)。
在图像处理领域,conv卷积层等提取图像初级特征,pooling层(最大池化、平均池化、k-max池化等)进行特征筛选保留主要特征、减少计算量、保证某种不变性等。但是标量计算往往会丢失很多信息,如论文所言,只要图片中有眼睛、嘴巴、鼻子,大约就可以认为这是一个人了,从而不管这些特征的相对位置,这是不合理的。
所以这才有了CapsuleNet,将pooling层的标量计算改为矢量计算,从而确保更多信息不丢失(如位置、姿态等)。
简单来说,我们可以把CapsuleNet看成一种向量输入向量输出的过程,无非就是多乘了个权重矩阵W么。又或者bonjoe理解的输出是输入的某种聚类。
github项目地址:
https://github.com/yongzhuo/Keras-TextClassification/tree/master/keras_textclassification/m13_CapsuleNet
二. CapsuleNet模型原理等
2.1 CapsuleNet模型图(capsule-net and squash)
2.2 compare(capsule calculate and common cnn)
这里采用github上项目CapsNet-Tensorflow的图像:
2.3 CapsuleNet主要思想
如上图所示,CapsuleNet的卷积层和普通的CNN大同小异,都是Conv1或者Conv2,没什么可说的。至于普通CNN的池化层pooling,则是变成了PrimaryCap和DigitCaps,具体说来,就是再接一个普通卷积(没什么可说的),然后再接激活函数squash(非全局,接近0时起放大作用,它就是一个激活函数),再就是"动态路由dynamic route"。
到这里你就发现,这个论文,就是为了达到矢量计算的目的,抛去新鲜的激活函数squash,可看的便是动态路由"dynamic route"了。
如果除去vector in vector out这点,那么,CapsuleNet让人觉得眼前一亮的便是它妄图抛去梯度下降了。如bonjoe所言,我感觉有些道理,或许去掉激活函数(压缩函数),网络也有非线性了(有动态路由)。
动态路由(dynamic route),本质上就是一个无穷数列,有迭代公式的那种。例如,以binjoe的观点,x = y + cosy,这样子的我们是求不出y=f(x)的。就像神经网络那样,融合的模型的你能用式子写出来么?超越方程,解不了不要紧,我们可以用迭代法,就像求数列那样。Y(n+1) = X - COSY(n),如果Y有界,那么多迭代几次,我们就能很接近极限值了。那么Y有界吗,我们不是用了激活函数限定取值范围吗,(0-1),这么看,激活函数还是很有用的,收回我说出去的话,squash函数还是有点重要的。
论文中的动态路由:
我也感觉像动态路由算法应该是bojnoe所说的Bij <- Uj|i * Vj那样,否则,单单从算法来看,那还不是和梯度下降差不多,至少从公式上来看,大同小异啊。
三.CapsuleNet实战
试验了github上的capsnet-keras,2K的star,但是不知道怎么回事,不管我如何调参,在NLP上总是得不到好的结果训练只有18%左右的准确率,后来采用bonjoe的capsule,微微调节就好了。应该是我水平比较差,哭。
github项目地址:
https://github.com/yongzhuo/Keras-TextClassification/tree/master/keras_textclassification/m13_CapsuleNet
核心代码:
# -*- coding: UTF-8 -*-
# !/usr/bin/python
# @time :2019/6/3 10:51
# @author :Mo
# @function :graph of CapsuleNet
from keras_textclassification.keras_layers.capsule import Capsule_bojone, CapsuleLayer, PrimaryCap, Length, Mask
from keras.layers import Conv1D, Conv2D, MaxPool2D, Concatenate, SpatialDropout1D
from keras.layers import Bidirectional, LSTM, GRU
from keras.layers import Dropout, Dense, Flatten
from keras.optimizers import Adam
from keras.layers import Reshape
from keras.models import Model
from keras import backend as K
import numpy as np
from keras_textclassification.base.graph import graph
class CapsuleNetGraph(graph):
def __init__(self, hyper_parameters):
"""
初始化
:param hyper_parameters: json,超参
"""
self.routings = hyper_parameters['model'].get('routings', 5)
self.dim_capsule = hyper_parameters['model'].get('dim_capsule', 16)
self.num_capsule = hyper_parameters['model'].get('num_capsule', 16)
super().__init__(hyper_parameters)
def create_model_cnn(self, hyper_parameters):
"""
构建神经网络
:param hyper_parameters:json, hyper parameters of network
:return: tensor, moedl
"""
super().create_model(hyper_parameters)
embedding = self.word_embedding.output
embed_layer = SpatialDropout1D(self.dropout)(embedding)
conv_pools = []
for filter in self.filters:
x = Conv1D(filters=self.filters_num,
kernel_size=filter,
padding='valid',
kernel_initializer='normal',
activation='relu',
)(embed_layer)
capsule = Capsule_bojone(num_capsule=self.num_capsule,
dim_capsule=self.dim_capsule,
routings=self.routings,
kernel_size=(filter, 1),
share_weights=True)(x)
conv_pools.append(capsule)
capsule = Concatenate(axis=-1)(conv_pools)
capsule = Flatten()(capsule)
capsule = Dropout(self.dropout)(capsule)
output = Dense(self.label, activation='sigmoid')(capsule)
self.model = Model(inputs=self.word_embedding.input, outputs=output)
self.model.summary(120)
def create_model(self, hyper_parameters):
"""
构建神经网络, bi-gru + capsule
:param hyper_parameters:json, hyper parameters of network
:return: tensor, moedl
"""
super().create_model(hyper_parameters)
embedding = self.word_embedding.output
embed_layer = SpatialDropout1D(self.dropout)(embedding)
x_bi = Bidirectional(GRU(self.filters_num,
activation='relu',
dropout=self.dropout,
recurrent_dropout=self.dropout,
return_sequences=True))(embed_layer)
# 一层
capsule = Capsule_bojone(num_capsule=self.num_capsule,
dim_capsule=self.dim_capsule,
routings=self.routings,
kernel_size=(3, 1),
share_weights=True)(x_bi)
# # pooling多层
# conv_pools = []
# for filter in self.filters:
# capsule = Capsule_bojone(num_capsule=self.num_capsule,
# dim_capsule=self.dim_capsule,
# routings=self.routings,
# kernel_size=(filter, 1),
# share_weights=True)(x_bi)
# conv_pools.append(capsule)
# capsule = Concatenate(axis=-1)(conv_pools)
capsule = Flatten()(capsule)
capsule = Dropout(self.dropout)(capsule)
output = Dense(self.label, activation='sigmoid')(capsule)
self.model = Model(inputs=self.word_embedding.input, outputs=output)
self.model.summary(120)
希望对你有所帮助!
参考:
抛开迷雾,来一场美味的Capsule盛宴: https://kexue.fm/archives/4819