Deeplab学习笔记

图像语义分割

语义分割方法在处理图像时需要具体到像素级别,也就是说语义分割会将图像中每个像素分配到某个对象类别。

如上图,左图为输入图像、右图为经过语义分割后的图像。语义分割不仅要识别出摩托车和驾驶者,还要标出每个对象的边界。因此与分类目的不同,相关模型要具有像素级的密集预测能力。

DCNN

2015年UC Berkeley 的Jonathan Long等人将CNN引入图像语义分割领域,开起了深度卷积网络在图像语义分割领域的新篇章。目前大多数图像语义分割都是基于基本的深度卷积网络实现,例如AlexNet、VGG16、ResNet101、Xception等。CNN的发展促进了语义分割的发展,CNN的发展如下图:

VGG16

VGG网络是在AlexNet网络的基础上发展而来的,其主要贡献在于使用非常小的3*3的卷积核进行网络设计,并且将网络深度增加到16-19层。在2014年ImageNet比赛中,获得了定位第1,分类第2的好成绩,网络具有很好的泛化能力。由牛津大学 Visual Geometry Group(视觉几何组)完成的论文,所以叫VGG。结构图如下:

分为A-LRN、A-E 6种结构,VGG16是指结构C,由13个卷积层和3个全连接层组成。VGG16具体结构图 

根据公式 H1 = ( H – kernel + 2 x padding ) / stride + 1

conv3采用 kernel = 3, stride = 1, padding = 1的方式,H1= ( H – 3 + 2 x 1 ) / 1 + 1 = H,故conv3卷积不改变图像(特征图)分辨率。

maxpool采用 kernel = 2, stride = 2, padding = 0的方式,H1 = ( H – 2 +2 x 0) / 2 + 1 = H / 2 ,故maxpool池化将图像缩小为原来的一半。

VGG输入是224 x 224的RGB图像,在经过5次maxpool后缩小为原来的1/32,变成7 x 7的特征图。

为什么采用conv3?

1.如下图,两个3 x 3的卷积层串联相当于一个5 x 5的卷积层(感受野大小为5 x 5)。而三个3 x 3的卷积层串联的效果则相当于一个7 x 7的卷积层。

2.除此之外,三个串联的3 x 3的卷积层,拥有比一个7 x 7的卷积层更少的参数量,只有后者的(3 x 3 x 3) / (7 x 7) = 55%。

3.三个3 x 3的卷积层拥有比一个7 x 7的卷积层更多的非线性变换(前者可以使用三次ReLU激活函数,而后者只有一次),使得CNN对特征的学习能力更强。


 

DCNN在语义分割中存在的问题

首先我们应该了解CNN在图像语义分割时需要解决的三个问题:

1.下采样(池化)过程中导致细节的丢失。

2.CNN的空间不变性,对于同一张图进行空间变换分类结果不变,但是语义分割要求随着空间变换,分割的区域不同。

3.多尺度目标的类别预测 。

Deeplab V1

针对CNN存在的两个问题本文采取的解决方案:

1.使用带孔卷积解决下采样问题

2.使用全连接CRF解决空间不变性问题

3.使用MLP结合多尺度特征解决多尺度目标的类别预测

 

本文采用VGG16修改而成,将VGG16模型中的全连接层全部转换成卷积层,由于VGG16的5次maxpool得到特征图为原图的1/32,作者的目标是得到一个1/8原图的特征图,所以移除最后两个maxpool,但是通过查看源码发现作者并没有移除最后两层maxpool,而是将参数修改成stride = 1 ,padding = 1,根据公式计算( H – 2 + 2 x 1) / 1 + 1 = H + 1,增加1像素,但是作者并没有处理,影响不大。

分辨率比之前提高,会导致最后一个卷积层的感受野相对变小,所以作者的idea是对原卷积核填充0,也就是hole,把kernel试着变大。

maxpool4的stride由2变为1,则紧接着的conv5_1,、conv5_2、conv5_3中hole size为2。

maxpool5由2变为1, 则后面的fc6中hole size为4。

此处实际为空洞卷积,如下图(hole size = 1):

在把VGG的后面的全连接层转换为卷积层之后,比如说第一个全连接层(即原来的FC6)现在的卷积kernel = 7,计算量会很大,作者直接从7×7的范围中抽取一个 4×4 (or 3×3)的范围以此来减小计算量,计算时间比原来减少了2-3倍。

DCNN的输出与GroundTruth下采样8倍做比较,损失函数采用交叉熵训练网络。DCNN模型训练完成后采用双线性插值法恢复到原图分辨率。如下图,得分图可以可靠地预测图像中对象的存在和粗略位置,但不太适合用于刻画精准的轮廓。

然后作者采用Fully Connected CRF改善分割细节。CRF简单来说,能做到的就是在决定一个位置的像素值时(在这个paper里是label),会考虑周围邻居的像素值(label),这样能抹除一些噪音。但是通过CNN得到的特征图在一定程度上已经足够平滑了,所以仅考虑周围邻居的CRF没什么意义。于是作者采用了Fully Connected CRF,这样考虑的就是全局的信息了。

双线性插值法

Fully Connected CRF

全连接的CRF模型使用的能量函数E(x)为:

这分为一元势函数θi(xi)和二元势函数θij(xi,xj)两部分。

一元势函数是定义在观测序列位置i的状态特征函数,用于刻画观测序列对标记变量的影响,故这里P(xi)是取DCNN计算关于像素i的输出的标签分配概率。

二元势函数是描述像素和像素之间的关系,如果比较相似,那可能是一类,否则就裂开,这可以细化边缘。一般的二元势函数只取像素点与周围像素之间的边,这里使用的是全连接,即像素点与其他所有像素之间的关系。

DeepLab由DCNN和CRF组成,训练策略是分段训练,即DCNN的输出是CRF的一元势函数,在训练CRF时是固定的。在对DCNN做了fine-tune后,对CRF做交叉验证。这里使用ω2=3σγ=3在小的交叉验证集上寻找最佳的ω1,σα,σβ,采用从粗到细的寻找策略。

解决多尺度预测问题

论文还探讨了使用多尺度预测提高边界定位效果。具体的,在输入图像和前四个最大池化层的输出上附加了两层的MLP(第一层是128个3×3卷积,第二层是128个1×1卷积),最终输出的特征映射送到模型的最后一层辅助预测,合起来模型最后的softmax层输入特征多了5×128=640个通道,实验表示多尺度有助于提升预测结果,但是效果不如CRF明显。

总结

DeepLab是结合了深度卷积神经网络(DCNNs)和概率图模型(DenseCRFs)的方法。在实验中发现DCNNs做语义分割时精准度不够的问题,根本原因是DCNNs的高级特征的平移不变性(即高层次特征映射,根源于重复的池化和下采样)。

针对信号下采样或池化降低分辨率,DeepLab是采用的atrous(带孔)算法扩展感受野,获取更多的上下文信息。分类器获取以对象中心的决策是需要空间变换的不变性,这天然的限制了DCNN的定位精度,DeepLab采用完全连接的条件随机场(CRF)提高模型捕获细节的能力。

除空洞卷积和CRFs之外,论文使用的tricks还有Multi-Scale features。其实就是U-Net和FPN的思想,在输入图像和前四个最大池化层的输出上附加了两层的MLP(第一层是128个3×3卷积,第二层是128个1×1卷积),最终输出的特征与主干网的最后一层特征图融合,特征图增加5×128=640个通道,实验表示多尺度有助于提升预测结果,但是效果不如CRF明显。

论文的模型基于VGG16,在Titan GPU上运行速度达到了8FPS,全连接CRF平均推断需要0.5s ,PASCAL VOC-2012 达到71.6% IOU accuracy。

注:

交并比(Intersection-over-Union,IoU),目标检测中使用的一个概念,是产生的候选框(candidate bound)与原标记框(ground truth bound)的交叠率,即它们的交集与并集的比值。最理想情况是完全重叠,即比值为1。

Deeplab V2

Deeplab V2可以看作Deeplab V1 的升级版,主要是增加了ASPP代替MLP解决多尺度目标的类别预测 ,同时V2除了采用VGG16还采用了ResNet101。

以往进行多尺度目标的类别预测,需要将不同尺度的图像输入DCNN,但计算量增加。

作者根据SPP的思想,在给定特征层使用不同的采样率进行重采样,使用具有不同采样率的平行atrous卷积层实现,称为atrous SPP(ASPP)。

ASPP(带洞空间金字塔池化)方法示意图如下图所示:

以VGG16为例,在maxpool5后采用(rate分别为6、12、18、24)空洞卷积,得到特征图后合成,在用softmax分类器分类。

总结

DeepLabv2是相对于DeepLabv1基础上的优化。DeepLabv1在三个方向努力解决,但是问题依然存在:特征分辨率的降低、物体存在多尺度,DCNN的平移不变性。

因DCNN连续池化和下采样造成分辨率降低,DeepLabv2在最后几个最大池化层中去除下采样,取而代之的是使用空洞卷积,以更高的采样密度计算特征映射。 物体存在多尺度的问题,DeepLabv1中是用多个MLP结合多尺度特征解决,虽然可以提供系统的性能,但是增加特征计算量和存储空间。论文受到我们受到spatial pyramid pooling(SPP)的启发,提出了一个类似的结构,在给定的输入上以不同采样率的空洞卷积并行采样,相当于以多个比例捕捉图像的上下文,称为ASPP模块。

DCNN的分类不变形影响空间精度。DeepLabv2是采样全连接的CRF在增强模型捕捉细节的能力。 论文的模型基于ResNet,在NVidia Titan X GPU上运行速度达到了8FPS,全连接CRF平均推断需要0.5s ,在耗时方面和DeepLabv1无差异,但在PASCAL VOC-2012 达到79.7 mIOU。

ResNet

MSRA何凯明团队的Residual Networks,在2015年ImageNet上大放异彩,在ImageNet的classification、detection、localization以及COCO的detection和segmentation上均斩获了第一名的成绩,而且Deep Residual Learning for Image Recognition也获得了CVPR2016的best paper。

ResNet最根本的动机就是所谓的“退化”问题,即当模型的层次加深时,错误率却提高了,如下图:

但是模型的深度加深,学习能力增强,因此更深的模型不应当产生比它更浅的模型更高的错误率。而这个“退化”问题产生的原因归结于优化难题,当模型变复杂时,SGD的优化变得更加困难,导致了模型达不到好的学习效果。

针对这个问题,作者提出了一个残差的结构,它使用了一种连接方式叫做“shortcut connection”,顾名思义,shortcut就是“抄近道”的意思,:

ResNet提出了两种mapping:第一种是identity mapping,指的就是上中”弯弯的曲线”,第二种residual mapping,指的就是除了”弯弯的曲线“那部分,所以最后的输出是 H(x)=F(x)+x
identity mapping顾名思义,就是指本身,也就是公式中的x,而residual mapping指的是“差”,也就是H(x) – x,所以残差指的就是F(x)部分。

为什么ResNet可以解决“随着网络加深,准确率不下降”的问题?

1.实验方面,实验结果如下图:

2.理论方面,ResNet提供了两种选择方式,也就是identity mapping和residual mapping,如果网络已经到达最优,继续加深网络,residual mapping将被push为0,只剩下identity mapping,这样理论上网络一直处于最优状态了,网络的性能也就不会随着深度增加而降低了。

作者提出了两种残差结构:

这两种结构分别针对ResNet34(左图)和ResNet50/101/152(右图),一般称整个结构为一个”building block“。其中右图又称为”bottleneck design”,目的一目了然,就是为了降低参数的数目,第一个1×1的卷积把256维channel降到64维,然后在最后通过1×1卷积恢复,整体上用的参数数目:1x1x256x64 + 3x3x64x64 + 1x1x64x256 = 69632,而不使用bottleneck的话就是两个3x3x256的卷积,参数数目: 3x3x256x256x2 = 1179648,差了16.94倍。
对于常规ResNet,可以用于34层或者更少的网络中,对于Bottleneck Design的ResNet通常用于更深的如101这样的网络中,目的是减少计算和参数量(实用目的)。

ResNet50/101结构:

F(x) 与 x 通道数不同如何相加,因为F(x)和x是按照通道维度相加的,通道不同怎么相加?

两种Shortcut Connection方式,分别为上图实线、虚线。

实线的Connection部分都是执行3x3x64的卷积,他们的channel个数一致,所以采用计算方式: H(x) = F(x) + x
虚线的Connection部分分别是3x3x64和3x3x128的卷积操作,他们的channel个数不同(64和128),所以采用计算方式: H(x) = F(x) + Ox,其中O是卷积操作1 x 1卷积用来统一通道数。

Deeplab V3

V3和V1 & V2相比改进的地方:

1.所提出的框架是通用的,可以应用于任何网络。

2.最后一个ResNet块(Block 4)的几个副本被复制,并以级联排列。

3.ASPP中使用了Batch Normalization

4.没有使用CRF

讨论多种捕获多尺度信息的方式:

(a). Image Pyramid: 将输入图片放缩成不同比例,分别应用在DCNN上,将预测结果融合得到最终输出。

(b). Encoder-Decoder: 利用Encoder阶段的多尺度特征,运用到Decoder阶段上恢复空间分辨率(代表工作有FCN、SegNet、PSPNet等工作)。

(c). Deeper w. Atrous Convolution: 在原始模型的顶端增加额外的模块,例如DenseCRF,捕捉像素间长距离信息。

(d). Spatial Pyramid Pooling: 空间金字塔池化具有不同采样率和多种视野的卷积核,能够以多尺度捕捉对象。

 

论文首先探讨将空洞卷积应用在级联模块。具体来说,我们取ResNet中最后一个block,在下图中为block4,并在其后面增加级联模块。

上图(a)所示,整体图片的信息总结到后面非常小的特征映射上,但实验证明这是不利于语义分割的。上图(b)所示,可使用不同采样率的空洞卷积保持输出步幅的为out_stride = 16.这样不增加参数量和计算量同时有效的缩小了步幅。

作者在DeepLabv3中ASPP中添加了BN层。不同采样率的空洞卷积可以有效的捕获多尺度信息,但是,我们发现随着采样率的增加,滤波器的有效权重(权重有效的应用在特征区域,而不是填充0)逐渐变小。如下图所示:

当我们不同采样率的3×3卷积核应用在65×65的特征映射上,当采样率接近特征映射大小时,3×3的滤波器不是捕捉全图像的上下文,而是退化为简单的1×1滤波器,只有滤波器中心点的权重起了作用。为了克服这个问题,我们考虑使用图片级特征。具体来说,我们在模型最后的特征映射上应用全局平均池化,将结果经过1×1的卷积,再双线性上采样得到所需的空间维度。

最终,我们改进的ASPP包括:

  • 一个1×1卷积和三个3×3的采样率为rates={6,12,18}的空洞卷积,滤波器数量为256,包含BN层。
  • 针对output_stride=16的情况。如下图(a)部分Atrous Spatial Pyramid Pooling。
  • 图像级特征,即将特征做全局平均池化,经过卷积,再融合。如下图(b)部分Image Pooling。

改进后的ASPP模块如下图所示:

总结

DeepLab延续到DeepLab V3,依然是在空洞卷积做文章,但是探讨不同结构的方向。

V1-V2都是使用带孔卷积提取密集特征来进行语义分割。但是为了解决分割对象的多尺度问题,deeplabv3设计采用多比例的带孔卷积级联或并行来捕获多尺度背景。

DeepLab V3将修改之前提出的带孔空间金字塔池化模块,该模块用于探索多尺度卷积特征,将全局背景基于图像层次进行编码获得特征,取得当时最优性能,在PASCAL VOC-2012 达到86.9mIOU。

Xception

Xception 是基于Inception 发展而来,是Extreme Inception(极端 Inception)的简称。

Inception最初提出的版本,其核心思想就是使用多尺寸卷积核去观察输入数据。举个例子,我们看某个景象由于远近不同,同一个物体的大小也会有所不同,那么不同尺度的卷积核观察的特征就会有这样的效果。于是就有了如下的网络结构图:

于是我们的网络就变胖了,增加了网络的宽度,同时也提高了对于不同尺度的适应程度。但是我们的网络变胖了的同时,计算量也变大了,所以我们就要想办法减少参数量来减少计算量。

我们采用如下方法减少参数量:

1.使用1 x 1卷积核对输入的特征图进行降维处理(PointWise Conv)

2.使用多个小卷积核代替大卷积核

提出了Inception v3,如下图所示:

在Inception基础上为了进一步降低参数量提出了Bottleneck结构

先对数据进行降维,再进行常规卷积核的卷积,最后对数据进行升维,ResNet就是采用了这种思想。

但是发现参数量还是很大,于是提出了Xception主要采用Depthwise Separable Conv方法。

1.分别按不同通道进行一次卷积(生成 输入通道数 张 Feature Maps)- DW
2.再将这些 Feature Maps 一起进行第二次卷积 – PW

Standard Convolution参数量2 x 3 x 3 x 3 = 54

Depthwise Separable Convolution参数量为2 x 3 x 3 + 2 x 1 x 1 x 3 = 18 + 6 = 24

Xception网络结构图:

其中 SeparableConv具体结构如下:

Deeplab V3+

主要贡献:

1. 提出DeepLabv3+,采用Encoder-Decoder结构(其实就是语义分割常用的下采样再上采样);

2. 该网络通过带孔卷积可以任意控制Encoder Feature的大小,有较好的尺度适应性;

3. 采用修改后的 Xception主干网络,并在ASPP(带孔卷积模块)和Decoder模块采用Depthwise Separable Convolution;

如图在Decoder部分,Encoder输出通过双线性上采样x4之后,和浅层通过1x1conv 降低通道数后得到的同尺度的特征图堆叠,之后再通过一个3×3 conv,上采样x4后恢复到原图大小。

与 Xception 不同的几点是:

  • 层数变深了。
  • 所有的最大池化都被替换成了 3×3 with stride 2 的 Separable Convolution。
  • 在每个 3×3 Depthwise Separable Convolution 的后面加了 BN 和 ReLU。

总结

DeepLab V3+继续在模型的架构上作文章,为了融合多尺度信息,引入语义分割常用的Encoder-Decoder。在Encoder-Decoder架构中,引入可任意控制编码器提取特征的分辨率,通过空洞卷积平衡精度和耗时。在语义分割任务中采用Xception 模型,在ASPP和解码模块使用Depthwise Separable Convolution,提高编码器-解码器网络的运行速率和健壮性,在 PASCAL VOC 2012 达到 89.0mIOU。

DeepLab V3+效果测试

源码地址:https://github.com/tensorflow/models/tree/master/research/deeplab

参考文献

本文参考了大量互联网资源,原文链接如下,如有遗漏请联系添加。本文作为个人学习笔记整理,如有错误,还望指正。

[1] https://zhuanlan.zhihu.com/p/27794982

[2] https://zhuanlan.zhihu.com/p/33796585

[3] https://blog.csdn.net/Teeyohuang/article/details/75214758

[4] https://blog.csdn.net/marsjhao/article/details/72955935

[5] https://blog.csdn.net/chunfengyanyulove/article/details/79089608

[6] https://blog.csdn.net/lanran2/article/details/79057994

[7] https://blog.csdn.net/wspba/article/details/56019373

[8] https://blog.csdn.net/teeyohuang/article/details/77193653

[9] https://blog.csdn.net/u011974639/article/details/79134409

[10] https://blog.csdn.net/Hibercraft/article/details/79499648

[11] https://www.davex.pw/2018/02/05/breadcrumbs-about-inception-xception

tensorflow 曲线拟合绘图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

def add_layer(inputs,in_size,out_size,activation_function=None):
     Weights = tf.Variable(tf.random_normal([in_size,out_size]))
     biases = tf.Variable(tf.zeros([1,out_size]) +0.1)
     Wx_plus_b = tf.matmul(inputs,Weights) + biases
     if activation_function is None:
         outputs = Wx_plus_b
     else:
         outputs = activation_function(Wx_plus_b)
     return outputs

x_data = np.linspace(-1,1,300)[:,np.newaxis]
noise = np.random.normal(0,0.05,x_data.shape)
y_data = np.square(x_data) - 0.5 + noise
#y_data = np.square(x_data) - 0.5

xs = tf.placeholder(tf.float32,[None,1])
ys = tf.placeholder(tf.float32,[None,1])

l1 = add_layer(xs,1,10,activation_function=tf.nn.relu)
prediction = add_layer(l1,10,1,activation_function=None)

loss = tf.reduce_mean(tf.reduce_sum(tf.square(ys - prediction),reduction_indices=[1]))
train_step = tf.train.GradientDescentOptimizer(0.05).minimize(loss)

init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)

# 绘制数据图像
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.scatter(x_data,y_data)
plt.ion()
plt.show()

for i in range(10000):
    sess.run(train_step,feed_dict={xs:x_data,ys:y_data})
    if i%50 == 0:
        print(sess.run(loss,feed_dict={xs:x_data,ys:y_data}))
        try:
            ax.lines.remove(lines[0])
        except Exception:
            pass
        prediction_value = sess.run(prediction,feed_dict={xs:x_data})
        lines = ax.plot(x_data,prediction_value,'r-',lw=5)
        plt.pause(0.1)

tensorflow 曲线拟合小例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import tensorflow as tf
import numpy as np

def add_layer(inputs,in_size,out_size,activation_function=None):
     Weights = tf.Variable(tf.random_normal([in_size,out_size]))
     biases = tf.Variable(tf.zeros([1,out_size]) +0.1)
     Wx_plus_b = tf.matmul(inputs,Weights) + biases
     if activation_function is None:
         outputs = Wx_plus_b
     else:
         outputs = activation_function(Wx_plus_b)
     return outputs

x_data = np.linspace(-1,1,300)[:,np.newaxis]
noise = np.random.normal(0,0.5,x_data.shape)
y_data = np.square(x_data) - 0.5 + noise

xs = tf.placeholder(tf.float32,[None,1])
ys = tf.placeholder(tf.float32,[None,1])

l1 = add_layer(xs,1,10,activation_function=tf.nn.relu)
prediction = add_layer(l1,10,1,activation_function=None)

loss = tf.reduce_mean(tf.reduce_sum(tf.square(ys - prediction),reduction_indices=[1]))
train_step = tf.train.GradientDescentOptimizer(0.05).minimize(loss)

init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)

for i in range(10000):
    sess.run(train_step,feed_dict={xs:x_data,ys:y_data})
    if i%50 == 0:
        print(sess.run(loss,feed_dict={xs:x_data,ys:y_data}))

辽大信息学院研究生获国际大奖,提出颠覆性AI算法

理想很丰满,现实很骨感

来都来了,看看吧。。。。

听说研究生院办了个文艺展演活动(蛋疼展演活动

作为辽大最大的工科学院(辽大是不是工科只有信息学院),我们不能落后啊!!!!

同学们踊跃报名(班委东拼西凑、威逼利诱),我们上了两个节目,《水手》《推销》

说好的华尔兹木有了,想脱单的小伙伴有没有进展啊、啊!!

演员不够、班委来凑、唱的了水手、演的了推销,厉不厉害(报名人不够,我们不上怎么办,下次大家踊跃点呀,拜托拜托!!!

排练同学们还是很积极的(有活动要守时啊,说好的6点,6点半来了,来了我们也欢迎啊!!!

班委群为了守时定规矩了,开会迟到一分钟10块钱啊!(你寝室有班委,他要开会的时候,一定要按住了,五五开怎么样~

排练还是很辛苦哒,(哲理301太冷了!!! 冻脚!冻脚

在礼堂表演是这样的,快找找你自己,找不着的话问问你前边的妹子为什么要挡住!!(传说水手帽9.9包邮

排练的时候是这样的,在哲理楼瑟瑟发抖,有对象的回去抱对象,没对象的怎么办。(说好的加分,真的假的,会不会加到空气中,算了原谅色!!

我说班长和团支书没事,你信不信!!!看看幸福的表情,看看旁边两个羡慕的神态~~~(有事没事我真不知道哈~别来打我~~分布式班长演讲,我问了几个问题她说要打我~~~

好不容易抢了包辣条,还得挨打,孙悟空的棒子(可能是拖把杆),小胖紧盯着他的辣条,表演完回来就吃了!对他吃的!!!

这是谁,谁这是,用了雕牌护肤水的妹子,我在那举着心,团支书在干吗(莫非是思考人生

罪魁祸首在这,这几个推销的(我要是他们我早跑了,谁这么听话,在这站着

感谢2017信息学院全体同学的共同努力,我们拿了一个二等奖一个三等奖(虽然是鼓励奖,有总比没有强吧,毕竟我们排练这么长时间了

2017年辽宁大学研究生文艺展演活动艺术表现奖公布 (瞅啥,点它

就这么多了,没看够,我博客里还有其他的,随便看看啦!!!

基于maven在IDEA下搭建elasticsearch搜索程序

pom.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>wang.linteng</groupId>
  <artifactId>es</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>es Maven Webapp</name>
  <url>http://maven.apache.org</url>

  <properties>
    <spring.version>4.2.5.RELEASE</spring.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.elasticsearch.client</groupId>
      <artifactId>transport</artifactId>
      <version>5.6.4</version>
    </dependency>

    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-to-slf4j</artifactId>
      <version>2.9.1</version>
    </dependency>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.24</version>
    </dependency>
  </dependencies>

  <build>
    <finalName>es</finalName>
  </build>
</project>

ES操作工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package wang.linteng.util;

import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class EsTool {

    public static final String CLUSTER_NAME ="linteng-es";
    public static final String INDEX_NAME = "news";

    private static TransportClient client;

    // 获取客户端
    public static TransportClient getClient(){
        Settings settings = Settings.builder()
                .put("cluster.name",CLUSTER_NAME)
                .put("client.transport.sniff", true)
                .build();
        client = new PreBuiltTransportClient(settings);
        try {
            client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9300));
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return client;
    }

    // 关闭客户端
    public static void closeClient(){
        if(client != null){
            client.close();
        }
    }

    // 获取索引名称
    public static String getIndexName(){
        return INDEX_NAME;
    }

}

运行界面

项目源码:https://github.com/wanglinteng/es

安装部署elasticsearch head

1.首先需要git,没有的话需要先安装

1
sudo apt-get install git

2.下载head源码

1
git clone git://github.com/mobz/elasticsearch-head.git

3.安装node,因为head是一个基于nodejs的项目,下载的地址:https://nodejs.org/en/download/
4.选择适合的版本,解压到相应的目录,然后配置环境变量在/etc/profile,然后执行source /etc/profile,如果运行报错重启系统再试一下

1
2
3
#NODE
export NODE_HOME=/opt/node-v8.9.1
export PATH=$PATH:$NODE_HOME/bin

5.测试node是否生效,正常会输出版本信息

1
2
3
echo $NODE_HOME
node -v
npm -v

6.安装grunt,grunt是一个构建工具,head5.x版本是通过grunt启动的,所以要安装,最后检查一下版本

1
2
npm install grunt-cli
grunt -version

7.在下载的目录里执行

1
npm install

8.最后启动

1
2
grunt server
nohup grunt server & #后台启动注意终端退出使用exit,否则后台服务会终止

9.访问http://localhost:9100

logstash-input-jdbc 数据从mysql导入到ES中

elasticsearch5.x版本logstash-input-jdbc的安装方式与以往不同

1.这里要用到的是Logstash的一个插件Logstash-input-jdbc,这个插件可以将数据库中的数据同步到elasticsearch的索引库中,并且还能做实时增量同步。因此要用这个插件首先得下载一个Logstash,下载地址:https://www.elastic.co/cn/downloads/logstash,这里下载的版本要与elasticsearch一致,所以这里下载的是5.6.4版本

2.下载完解压到指定目录,进入目录输入

1
2
cd bin
./logstash -e 'input { stdin { } } output { stdout {} }'

3.随便输入hello world,会打印出来提示信息,代表可以正常运行

4.logstash是基于ruby开发的,如果没有gem,那么先安装gem,注意使用root用户

1
sudo apt-get install gem

5.查看是否gem安装成功

1
gem sources -l

6.替换ruby镜像库为国内的库,因为国外的库,国内是访问不到的,然后国内有两个库,淘宝的库已经停止支持了,使用ruby-china的库

1
gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/

7.查看是否成功

1
gem sources -l

8.然后进入bin目录,安装logstash-input-jdbc

1
./plugin install logstash-input-jdbc

9.安装成功后进行数据导入,需要mysql的驱动、mysql.conf、mysql.sql三个文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#mysql.conf
input {
    jdbc {
      jdbc_driver_library => "/opt/elasticsearch/logstash/bin/mysql/mysql-connector-java-5.1.44-bin.jar"
      jdbc_driver_class => "com.mysql.jdbc.Driver"    
      jdbc_connection_string => "jdbc:mysql://localhost:3306/news"
      jdbc_user => "root"
      jdbc_password => ""
      statement_filepath => "/opt/elasticsearch/logstash/bin/mysql/mysql.sql"
      jdbc_paging_enabled => "true"
      jdbc_page_size => "50000"
     
    }
}


output {
    stdout {
        codec => rubydebug
    }
    elasticsearch {
        hosts => "localhost:9200"
        index => "news"
    }
}

这里的sql是mysql的查询语句,article是mysql的表名

1
2
#mysql.sql
SELECT * FROM article

10.执行导入

1
./logstash -f ./mysql/mysql.conf

elasticsearch伪集群(单机多节点)安装

1.安装ES前要检查是否已经安装过java环境,java -version

2.下载ES,这里选择的是历史版本5.6.4,下载地址:https://www.elastic.co/downloads/past-releases

3.将下载的压缩包解压后复制三份分别命名为es-node1、es-node2、es-node3

4.分别修改三个目录下的elasticsearch.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# ======================== Elasticsearch Configuration =========================
#
# NOTE: Elasticsearch comes with reasonable defaults for most settings.
#       Before you set out to tweak and tune the configuration, make sure you
#       understand what are you trying to accomplish and the consequences.
#
# The primary way of configuring a node is via this file. This template lists
# the most important settings you may want to configure for a production cluster.
#
# Please consult the documentation for further information on configuration options:
# https://www.elastic.co/guide/en/elasticsearch/reference/index.html
#
# ---------------------------------- Cluster -----------------------------------
#
# Use a descriptive name for your cluster:
#
cluster.name: linteng-es
#
# ------------------------------------ Node ------------------------------------
#
# Use a descriptive name for the node:
#
node.name: node-1(修改成对应的名字)
#
# Add custom attributes to the node:
#
#node.attr.rack: r1
#
# ----------------------------------- Paths ------------------------------------
#
# Path to directory where to store the data (separate multiple locations by comma):
#
path.data: /opt/elasticsearch/es-node1/data(修改成对应的目录)
#
# Path to log files:
#
path.logs: /opt/elasticsearch/es-node1/logs
#
# ----------------------------------- Memory -----------------------------------
#
# Lock the memory on startup:
#
#bootstrap.memory_lock: true
#
# Make sure that the heap size is set to about half the memory available
# on the system and that the owner of the process is allowed to use this
# limit.
#
# Elasticsearch performs poorly when the system is swapping the memory.
#
# ---------------------------------- Network -----------------------------------
#
# Set the bind address to a specific IP (IPv4 or IPv6):
#
#network.host: 0.0.0.0
#
# Set a custom port for HTTP:
#
http.port: 9200/9201/9202(三个文件分别修改成不同的端口,避免冲突)
http.cors.enabled: true
http.cors.allow-origin: "*"
transport.tcp.port: 9300/9301/9301
#
# For more information, consult the network module documentation.
#
# --------------------------------- Discovery ----------------------------------
#
# Pass an initial list of hosts to perform discovery when new node is started:
# The default list of hosts is ["127.0.0.1", "[::1]"]
#
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]
#
# Prevent the "split brain" by configuring the majority of nodes (total number of master-eligible nodes / 2 + 1):
#
discovery.zen.minimum_master_nodes: 2
#
# For more information, consult the zen discovery module documentation.
#
# ---------------------------------- Gateway -----------------------------------
#
# Block initial recovery after a full cluster restart until N nodes are started:
#
#gateway.recover_after_nodes: 3
#
# For more information, consult the gateway module documentation.
#
# ---------------------------------- Various -----------------------------------
#
# Require explicit names when deleting indices:
#
#node.max_local_storage_nodes: 2
#action.destructive_requires_name: true

搜狗新闻数据集

搜狗实验室的中文新闻数据集,数据量大概在129万条左右,下载地址:http://www.sogou.com/labs/resource/ca.php

数据集的格式:

1
2
3
4
5
6
7
<doc>
<url>页面URL</url>
<docno>页面ID</docno>
<contenttitle>页面标题</contenttitle>
<content>页面内容</content>
</doc>
注意:content字段去除了HTML标签,保存的是新闻正文文本

注意数据集的编码格式为ANSI,在进行数据导入的时候注意格式转换。

采用Navicat导入mysql数据库,导入向导时注意选择编码格式否则会产生乱码。

我的考研路-痛并快乐着

写在前面

坐在图书馆看着毕老师的JAVA教程,虽然不是第一次看了,但是想系统的学一遍。看着周围正在准备考研的学弟学妹们,刚过去不久的考研画面,不断地浮现在我的脑海里。考研我并不是一个成功者,但是这段经历一直在影响我,所以我决定写下来,写在我的博客上!

准备考研

大三刚开学的时候,我一直在犹豫要不要考研。因为一直在孟老师的工作室写程序,大多是一些网站、微信平台,期间也能拿到一些报酬。其实自我感觉良好,关于上是否考研这件事,我想了很久,分析了利弊。其实我感觉如果不参加考研,一直做开发的话,找一个一线的互联网公司可能会有点困难,但是稍微差一点的应该没什么问题。大概大三上学期快结束的时候,我下定决心要考研,当时一心要考学硕,之前有工作室的学长考某某大学,所以刚开始我的目标就很明确,某某大学-计算机应用技术-学硕。考研更重要的是这段经历,我相信每个考研人都会有这样的感觉。寒假开始前我买了一本陈文灯的指南,基本没怎么看,可以说一直在睡觉。

寒假刚开始,因为课设是PHP,之前正好有写过的项目,直接拿过来改一改给魏老师了,他也没为难我。然后跟同学(江西)一起去了一趟哈尔滨,记得刚到哈尔滨的啥时候,江西的手机被冻关机了,开始还说苹果不行,后来走着走着我电话也关机了,第一次感受到了哈尔滨的寒冷。然后寒假回家,书基本没怎么看,但是也不知道从哪开始复习,只知道我要考研。

复习考研

寒假返校后,我记得我去考研楼(现在属于创新学院,正在装修中)三楼占了个位置,这是我第二次来考研楼,第一次是上一届考研结束的那天晚上,我跟江西过来复习期末考试。英语买了于慧真题100套、数学买了李永乐全套(其实没怎么看),然后每天除了上课就去考研楼看看书,其实效率很低。其实主要看数学这块,看张宇的视频记笔记。但是暑假都开始了,我高数刚看完、线代看了一点点,概率论完全没看,我当时很着急,觉得完了,他们都看得那么多(其实这就是我后来数学没考好的重要原因之一),然后专业课C语言看了一遍,觉得有些细节部分,之前没怎么注意。

暑假刚开始的时候,我就在考研楼二楼占了一个小屋子,两张桌子。暑假除了吃饭、睡觉、就是在那个屋子里面待着。在这给后来人一些建议,如果你要考研,就要考虑一下,你的感情是不是稳定的。在这个期间你的压力是很大的,会经常出现一些不开心的时候,如果你的另一半能体谅你,陪你一起奋斗,那是没有问题,身边就有这种成功的例子。如果你的感情还不稳定,会经常吵架,甚至动不动还会分手,那就早早分开好了!

我的英语很差的,我觉得没有人会比我的差,我考了6次四级,都是400分多一点,曾经跟朋友开玩笑说我的四级成绩可以当服务器错误代码了。最后一次通过是因为考研时复习了,除了于慧100套,还有就是何凯文的公众号,每天晚上都会有推送,事实上最后那100篇,我只研读了50篇不到,考研英语成绩和四六级真的没多少关系。

暑假结束,大概数学第一遍看完了。也开始了专业课复习,买了学长的专业课真题,答案错误的太多,差不多每个题我都会做完发到博客上,10月份开始陆陆续续看了点政治,买了肖秀荣全套。总体来说分配给专业课、政治的时间有点多,这也是我数学成绩差的一个重要原因。

从8月中旬开始一直到考研结束我去过最远的地方是考研报名那个地方,报名的时候我记得是和盟哥一起去的,当时怕去晚了要排队,去的很早,那天雪下的很大,去的时候打车去的,比较偏僻的一个地方,回来的时候坐公交回来的,车上都是去报名的学生,那个时候大家都一样,对未来的结果很憧憬也很怕。中间参加了一次同学聚会,然后在我的记忆里就没出过校门,没做过地铁。考研楼-食堂-寝室,晚上会到2楼的自习室看书,最早凌晨回寝室,晚点可能要凌晨2、3点了。那段时间真的快要扛不住了,感觉还有好多东西不会,越复习感觉希望越渺茫,但是还是坚持了下来,这段时间要感谢珊珊的鼓励、感谢盟哥的陪伴、感谢室友的支持理解。

除了上面说了两个原因,我觉得数学没考好的原因还有就是套题做的不够多,考试前就做了4、5套按照考试时间做的题,然后张宇的题很难,也都做的不怎么样。

快考试的前20天,开始背政治,肖四,每天晚上会到一楼走廊的那个拐角的那个位置背,很冷。大概每天晚上背完政治,看完何凯文的每日一句,在回寝室。那种感觉真的很难用语言去描述,有的时候,我跟室友大概两三天都见不着面,早上他还没醒我就去考研楼了,晚上都是凌晨以后我才回来,回来的时候他已经睡着了。

考研考场

之前就定了考研的房间,很贵800块住两晚,12月22日 21:49从考研楼出来我发了一条朋友圈。

然后第二天上午,看了会书,和盟哥一起坐大巴车去考场,走的时候。我记得在桌子上留了一个纸条,写的是不管结果如何,都是我人生的一部分,考完试我坦然的面对任何结果!其实结果并不是这样的。去的路上跟之前一样晕车了,没吃晕车药,我怕吃了就背不了题了。盟哥给了我一个苹果,坚持到了酒店,很破,就是小旅馆的水平。

第二天上考场一切很顺利,政治、英语发挥的都很正常。晚上回酒店,感觉很多公式都忘了,背了背,考数学题跟我想象的不太一样,彻底慌了,最后15分钟,根本就写不下去,有的不是不会,我当时觉得完了,没戏了!

中午回来,盟哥在考场那等我,我们都问了对方一句,下午还去吗?我说不想吃饭了,给我妈打了电话说没有希望了。盟哥买了两个苹果,到了酒店躺一会,吃了苹果,退了房间下午去考场,很冷在外面等着。专业课考完了,收完卷,盟哥还有江西坐在我后面,我跟他们说我不甘心,因为专业课发挥的很好,考的虽然难,但是我都会。

找到了回学校的大巴车,背着沉重的书包,那个时候天已经黑了,同样是晕车,我跑到了司机旁边的座位,望着窗外,心里很难受,我永远忘不了那种感觉。下车整个人都不行了,吐了一会,盟哥把我扶上楼,回到寝室,我觉得自己好失败,真的好难受那时候。盟哥买了春饼,室友带了吃的,我吃了一些。后来到放假前基本就是看了一些PHP、Mysql一些比较深入的东西。

调剂之路

2月13号,晚上坐飞机去北京找工作,约了很多家,大概上午一家下午一家。小姑父开车送的我,去的路上,车还碰了一下,刚换的新车,也挺不好意思的。在北京待了三天收到了三个offer,心里还是挺高兴的,期间要特别感谢锡文帮我解决了住宿问题。15号查成绩的时候数学比预想的好一点,其他三门跟想象的差不多,我觉得有希望调剂。拒绝了offer,晚上坐火车回沈阳。。。

然后就是调剂,这绝对是一个痛苦的事情,连续几天就是打电话,问调剂的事情,上网查资料。联系了某很强的211学校,老师表示特别愿意要我,聊得也非常好,短信、邮件沟通了好久。我在网上找了复试的资料,看了一个月。期间除了看调剂群,就是复习复试。调剂群里,真的什么样的同学都有,我觉得每一个考研的同学,在考研前都应该看看上一届的调剂群。有的时候真的是选择大于努力,考研不是一个你努力就可以成功的事情,尤其计算机400多分被刷下来的也有很多。那段日子每天都像赌博一般,生怕错过群里一条有用的信息。

后来准备复试的那个学校老师跟我说,竞争很大,但是可以试一试。也联系了上海的一所普本,还有一所普通的211学校,期间也有其他学校让我填调剂系统,因为不太能接受,都没有填。后来调剂系统开放那天晚上,在电脑前守到了凌晨1点,从12点开始崩溃,然后一直刷,眼睛都不敢离开屏幕。最后事实证明我是对的,填的越早收到复试的可能性越大,有比我分还高的填同一个学校没有收到复试通知。我填了强211、普通211、上海普本,真的跟赌博一样。然后睡觉去了。第二天看到调剂群里很多人都接到了复试通知,我的心开始慌了。一直等到晚上,强211学硕名额为0,专硕少量,本科985,分数高的学生一大批都在盯着,上海普本告诉我一共六个名额,大佬很多,还有本校保护,普通211没有音讯,况且这三个学校48小时之内不能动,但是过了48小时基本就没什么学校了。当时的心情真的难以描述!我吃晚饭的时候收到了普通211的复试通知,心里好像有点底了,其他两个学校后来拒绝我,我改填了别的学校保底。

然后就是复试,笔试是之前初试考过的没什么问题,面试抽的三道题有我准备强211复试的内容也没问题,英语面试我写的自我介绍啥的小蔓蔓帮我改过了,非常感谢,很顺利的通过了。复试完事第二天发了拟录取通知,接受了,我的考研之路也就结束了。

写在最后

首先要特别感谢恩师孟老师,让我在大学找到了自己的兴趣所在,也为我提供了非常好的实践平台,在考研调剂之路给予我特别大的帮助和支持!

其次要感谢那些帮助支持我的人,就不一一点名了。

最后送给大家两句话:

1.选择和方向永远比努力更重要!

2.相信自己、相信奇迹、不到最后一刻千万不能放弃!