nndl 读书笔记 第4章 前馈神经网络

github开源:https://nndl.github.io/
nndl作者:邱锡鹏
笔记作者:Isaac.(原创)

本书其他章节的笔记目录

我们主要关注于采用误差反向传播来进行学习的神经网络。

神经网络一般可以看作是一个非线性模型,其基本组成单位为具有非线性激活函数的神经元。

神经元之间的连接权重就是需要学习的参数,可以通过梯度下降方法来进行学习。

目录结构

4.1 神经元
4.1.1 Sigmoid型激活函数
4.1.2 修正线性单元
4.1.3 Swish函数
4.1.4 高斯误差线性单元
4.1.5 Maxout单元
4.2 网络结构
4.2.1 前馈网络
4.2.2 记忆网络
4.2.3 图网络
4.3 前馈神经网络
4.3.1 通用近似定理
4.3.2 应用到机器学习
4.3.3 参数学习
4.4 反向传播算法
4.5 自动梯度计算
4.5.1 数值微分
4.5.2 符号微分
4.5.3 自动微分
4.6 优化问题
4.6.1 非凸优化问题
4.6.2 梯度消失问题
4.7 总结和深入阅读

4.1 神经元

人工神经元(Artificial Neuron),简称神经元(Neuron),是构成神经网络的基本单元,其主要是模拟生物神经元的结构和特性,接受一组输入信号并产出输出

假设一个神经元接受 d个输入x_1,x_2,…,x_d,令向量 x=[x_1;x_2;…;x_d] 来表示这组输入,并用净输入(Net Input)z ∈ R表示一个神经元所获得的输入信 号 x的加权和
\begin{split} z &= \displaystyle \sum^{d}_{i=1}{w_ix_i+b}\\ \\ &= w^Tx+b \end{split}
其中 w=[w_1;w_2;…;w_d] \in R^d 是 d维的权重向量b \in R偏置

净输入 z 在经过一个非线性函数 f(·)后,得到神经元的活性值(Activation)a
a=f(z)
其中非线性函数 f(·)称为激活函数(Activation Function)

典型的神经元结构

激活函数在神经元中非常重要的。

激活函数需要具备以下几点性质:

  1. 连续并可导(允许少数点上不可导)的非线性函数。可导的激活函数可以直接利用数值优化的方法来学习网络参数。
  2. 激活函数及其导函数要尽可能的简单,有利于提高网络计算效率。
  3. 激活函数的导函数的值域要在一个合适的区间内,不能太大也不能太小,否则会影响训练的效率和稳定性。

4.1.1 Sigmoid型激活函数

Sigmoid型函数是指一类S型曲线函数,为两端饱和函数。常用的 Sigmoid型函数有 Logistic函数Tanh函数

对于函数 f(x),若 x → -∞时,其导数 f ′(x) → 0,则称其为左饱和。若 x → +∞时,其导数 f ′(x) → 0,则称其为右饱和。当同时满足左、右饱和时,就称为两端饱和。

Logistic函数定义为
\begin{split} \sigma(x) &= {\frac{1}{1+e^{-x}}}\\ \\ \sigma(x)' &= \sigma(1-\sigma) \end{split}
Logistic函数可以看成是一个“挤压”函数,把一个实数域的输入“挤压”到(0, 1)

Tanh 函数 定义为

\begin{split} tanh(x) &= {\frac{e^x-e^{-x}}{e^x+e^{-x}}}\\ \\ tanh(x)' &= 1 - tanh^2(x) \end{split}
Tanh函数可以看作是放大并平移的 Logistic函数,其值域是 (-1, 1)
tanh(x)=2\sigma(2x)-1

Logistic函数和 Tanh函数

Tanh 函数的输出是零中心化的(Zero-Centered),而 Logistic函数的输出恒大于 0。

非零中心化的输出会使得其后一层的神经元的输入发生偏置偏移(Bias Shift),并进一步使得梯度下降的收敛速度变慢

Logistc求导过程:
\sigma = \frac{1}{1 + e^{-x}}

t = e^{-x}

\sigma = {\frac{1}{1+t}}
分别求导
\sigma_t' = - {\frac{1}{(1+t)^2}}

t_x' = - e^{-x}

\sigma_x' = {\frac{e^{-x}}{(1+e^{-x})^2}}

化简
\begin{split} \sigma' &= {\frac{1+e^{-x}-1}{(1+e^{-x})^2}} \\ &= {\frac{1}{1+e^{-x}}}-{\frac{1}{(1+e^{-x})^2}} \\ &= {\frac{1}{1+e^{-x}}}(1-{\frac{1}{1+e^{-x}}}) \\ &= \sigma(1-\sigma) \end{split}

Tanh的求导过程:


a = e^x \qquad b=e^{-x}
化简
\begin{split} tanh(x) &= {\frac{e^x-e^{-x}}{e^x+e^{-x}}}\\ &={\frac{a-b}{a+b}}\\ &={\frac{(a-b)'\times(a+b)-(a-b)\times(a+b)'}{(a+b)^2}} \end{split}
因为
\begin{split} (a-b)'&=(e^x-e^{-x})' = e^x+e^{-x}=a+b\\ (a+b)'&=(e^x+e^{-x})' = e^x-e^{-x} = a-b \end{split}
代回原式
\begin{split} tanh(x)'&={\frac{(a+b)^2-(a-b)^2}{(a+b)^2}}\\ &=1-{\frac{(a-b)^2}{(a+b)^2}}\\ &=1-({\frac{e^x-e^{-x}}{e^x+e^{-x}}})^2\\ &=1-tanh^2(x) \end{split}

补充资料:激活函数(Sigmoid, tanh, Relu)

4.1.1.1 Hard-Logistic 和 Hard-Tanh 函数

Logistic函数和 Tanh函数都是 Sigmoid型函数,具有饱和性,但是计算开销较大。因为这两个函数都是在中间(0附近)近似线性,两端饱和。因此,这两个函数可以通过分段函数来近似

4.1.2 修正线性单元

修正线性单元(Rectified Linear Unit, ReLU)是目前深层神经网络中经常使用的激活函数。ReLU实际上是一个斜坡(ramp)函数,定义为
\begin{split} ReLU(x)&= \begin{cases} x& {x\geq 0}\\ 0& {x<0} \end{cases}\\ &=\max(0,x) \end{split}

ReLU(x)' = \begin{cases} 1& {x>0}\\ 不可导& {x=0}\\ 0& {x<0} \end{cases}

ReLU零点导数可以人为规定,比如tensorflow中规定为0,也可以用其他函数来近似,比如取0.5。

优点

  • 采用 ReLU 的神经元只需要进行比较的操作,计算上更加高效
  • ReLU函数被认为有生物上的解释性,比如单侧抑制宽兴奋边界(即兴奋程度也可以非常高)。在生物神经网络中,同时处于兴奋状态的神经元非常稀疏。人脑中在同一时刻大概只有 1 ∼ 4%的神经元处于活跃状态。 Sigmoid 型激活函数会导致一个非稀疏的神经网络,而 ReLU却具有很好的稀疏性,大约 50%的神经元会处于激活状态。
  • 在优化方面,相比于 Sigmoid型函数的两端饱和, ReLU函数为左饱和函数,且在 x > 0时导数为 1,在一定程度上缓解了神经网络的梯度消失问题,加速梯度下降的收敛速度

缺点

  • ReLU函数的输出是非零中心化的,给后一层的神经网络引入偏置偏移会影响梯度下降的效率
  • 此外, ReLU 神经元在训练时比较容易“死亡”。在训练时,如果参数在一次不恰当的更新后,第一个隐藏层中的某个 ReLU 神经元在所有的训练数据上都不能被激活,那么这个神经元自身参数的梯度永远都会是0,在以后的训练过程中永远不能被激活。这种现象称为死亡 ReLU 问题(Dying ReLU Problem),并且也有可能会发生在其它隐藏层。

为什么使用 ReLU

4.1.2.1 带泄露的 ReLU

带泄露的 ReLU(Leaky ReLU)在输入 x < 0时,保持一个很小的梯度。这样当神经元非激活时也能有一个非零的梯度可以更新参数,避免永远不能被激活。
\begin{split} LeakyReLU(x)&= \begin{cases} x& {x>0}\\ \gamma x& {x\leq 0} \end{cases}\\ &=\max(0,x)+\gamma \min(0,x) \end{split}
其中 \gamma是一个很小的常数,比如 0.01。当 \gamma<1 时,带泄露的 ReLU也可以写为
LeakyReLU(x)=max(x,\gamma x )
相当于是一个比较简单的 maxout单元。

4.1.2.2 带参数的 ReLU

带参数的 ReLU(Parametric ReLU, PReLU)引入一个可学习的参数,不同神经元可以有不同的参数。对于第 i 个神经元,其 PReLU 的定义为
\begin{split} PReLU_i(x)&= \begin{cases} x& {x>0}\\ \gamma_i x& {x\leq 0} \end{cases}\\ &=\max(0,x)+\gamma_i \min(0,x) \end{split}
如果 \gamma_i= 0,那么PReLU就退化为 ReLU。如果\gamma_i 为一个很小的常数,则 PReLU可以看作带泄露的 ReLU。

4.1.2.3 ELU

指数线性单元(Exponential Linear Unit, ELU)是一个近似的零中心化的非线性函数,其定义为
\begin{split} ELU(x)&= \begin{cases} x& {x>0}\\ \gamma(e^x-1)& {x\leq 0} \end{cases}\\ &=\max(0,x)+\min(0,\gamma(e^x-1)) \end{split}
其中 \gamma \geq 0 是一个超参数,决定 x ≤ 0时的饱和曲线,并调整输出均值在 0附近。

4.1.2.4 Softplus 函数

Softplus 函数可以看作是ReLU函数的平滑版本,其定义为
Softplus(x)=log(1+e^x)
Softplus函数其导数刚好是 Logistic函数。Softplus函数虽然也有具有单侧抑制、宽兴奋边界的特性,却没有稀疏激活性

ReLU、Leaky ReLU、ELU以及 Softplus函数

4.1.3 Swish函数

Swish 函数是一种自门控(Self-Gated)激活函数,定义为
swish(x)=x\sigma(\beta x)
其中 \sigma(·) 为 Logistic函数, β 为可学习的参数或一个固定超参数。

\sigma(·) \in (0,1) 可以看作是一种软性的门控机制。当 \sigma(\beta x) 接近于 1时,门处于“开”状态,激活函数的输出近似于x本身;当 \sigma(\beta x) 接近于 0时,门处于“关”状态,激活函数的输出近似于 0。
当 β = 0时, Swish函数变成线性函数 x/2。

当 β = 1时, Swish函数在 x > 0时近似线性,在 x < 0时近似饱和,同时具有一定的非单调性。

当 β → +∞时, \sigma(\beta x)趋向于离散的 0-1函数, Swish函数近似为ReLU函数。

因此,Swish函数可以看作是线性函数和 ReLU函数之间的非线性插值函数,其程度由参数 β 控制。

Swish函数

4.1.4 高斯误差线性单元

高斯误差线性单元(Gaussian Error Linear Unit, GELU)和 Swish 函数比较类似,也是一种通过门控机制来调整其输出值的激活函数。
GELU(x)=xP(X \leq x)
其中 P(X ≤ x)是高斯分布 N(µ, σ2)的累积分布函数,其中 µ, σ 为超参数,一般设 µ = 0, σ = 1即可。由于高斯分布的累积分布函数为 S型函数,因此 GELU可以用 Tanh函数或 Logistic函数来近似,
\begin{split} &GELU(x) \approx 0.5x\bigg(1+tanh\Big(\sqrt{\frac{2}{\pi}}( x+0.044715x^3)\Big)\bigg)\\ &GELU(x) \approx x\sigma(1.702x) \end{split}
当使用 Logistic函数来近似时,GELU相当于一种特殊的 Swish函数。

4.1.5 Maxout单元

Maxout单元也是一种分段线性函数。Sigmoid型函数、 ReLU等激活函数的输入是神经元的净输入 z,是一个标量。而 Maxout单元的输入上一层神经元的全部原始输出,是一个向量 x=[x_1;x_2;…;x_d]

每个 Maxout单元有K 个权重向量 w_k \in R^d 和偏置b_k (1 ≤ k ≤ K)。对于输入 x,可以得到 K 个净输入z_k, 1 ≤ k ≤ K
z_k = w_k^Tx+b_k
其中w_k = [w_{k,1}, · · · , w_{k,d}]^T 为第 k 个权重向量。

Maxout单元的非线性函数定义为
maxout(x)= \max\limits_{k\in[1,K]}(z_k)
Maxout单元是整体学习输入到输出之间的非线性映射关系。

Maxout激活函数可以看作任意凸函数的分段线性近似,并且在有限的点上是不可微的。

Maxout激活函数原理及实现

激活函数(ReLU, Swish, Maxout)

4.2 网络结构

4.2.1 前馈网络

前馈网络中各个神经元按接受信息的先后分为不同的组。每一组可以看作一个神经层

每一层中的神经元接受前一层神经元的输出并输出到下一层神经元。整个网络中的信息是朝一个方向传播,没有反向的信息传播,可以用一个有向无环路图表示。

前馈网络可以看作一个函数,通过简单非线性函数的多次复合,实现输入空间到输出空间的复杂映射。这种网络结构简单,易于实现。

4.2.2 记忆网络

记忆网络,也称为反馈网络,网络中的神经元不但可以接收其它神经元的信息,也可以接收自己的历史信息。和前馈网络相比,记忆网络中的神经元具有记忆功能,在不同的时刻具有不同的状态。记忆神经网络中的信息传播可以是单向或双向传递,因此可用一个有向循环图或无向图来表示。记忆网络包括循环神经网络,Hopfield网络、玻尔兹曼机等。
记忆网络可以看作一个程序,具有更强的计算和记忆能力。
为了增强记忆网络的记忆容量,可以引入外部记忆单元读写机制,用来保存一些网络的中间状态,称为记忆增强神经网络(Memory-Augmented NeuralNetwork, MANN) ,比如神经图灵机和记忆网络等。

4.2.3 图网络

前馈网络和记忆网络的输入都可以表示为向量或向量序列。但实际应用中很多数据是图结构的数据,比如知识图谱社交网络、分子(Molecular )网络等。前馈网络和反馈网络很难处理图结构的数据。

图网络是定义在图结构数据上的神经网络。图中每个节点都由一个或一组神经元构成。节点之间的连接可以是有向的,也可以是无向的。每个节点可以收到来自相邻节点或自身的信息。
图网络是前馈网络和记忆网络的泛化,包含很多不同的实现方式,比如 图卷积网络(Graph Convolutional Network,GCN)图注意力网络(Graph Attention Network, GAT)消息传递网络(Message Passing Neural Network,MPNN)等。

三种不同的网络模型

图神经网络为何如此强大?看完这份斯坦福31页PPT就懂了!

4.3 前馈神经网络

第0层叫输入层,最后一层叫输出层,其它中间层叫做隐藏层。整个网络中无反馈,信号从输入层向输出层单向传播,可用一个有向无环图表示。

  • L:表示神经网络的层数
  • m^{(l)}:表示第 l 层神经元的个数
  • f_l(\cdot):表示 l 层神经元的激活函数
  • W^{(l)}:表示 l - 1层到第 l 层权重矩阵
  • b^{(l)}:表示 l - 1层到第 l 层偏置
  • z^{(l)} :表示 l 层神经元的净输入(净活性值);
  • a^{(l)}:表示 l 层神经元的输出(活性值)。

前馈神经网络通过下面公式进行信息传播,
\begin{split} z^{(l)} &= W^{(l)} \cdot a^{(l-1)}+b^{(l)}\\ a^{(l)} &= f_l(z^{(l)}) \end{split}

4.3.1 通用近似定理

根据通用近似定理,对于具有线性输出层和至少一个使用“挤压”性质的激活函数的隐藏层组成的前馈神经网络,只要其隐藏层神经元的数量足够,它可以以任意的精度来近似任何从一个定义在实数空间 Rd 中的有界闭集函数。

通用近似定理只是说明了神经网络的计算能力可以去近似一个给定的连续函数,但并没有给出如何找到这样一个网络,以及是否是最优的。

4.3.2 应用到机器学习

根据通用近似定理,神经网络在某种程度上可以作为一个“万能”函数来使用,可以用来进行复杂的特征转换,或逼近一个复杂的条件分布。

在机器学习中,输入样本的特征对分类器的影响很大。以监督学习为例,好的特征可以极大提高分类器的性能。因此,要取得好的分类效果,需要样本的原始特征向量 x转换到更有效的特征向量 φ(x),这个过程叫做特征抽取

4.3.3 参数学习

梯度下降法需要计算损失函数对参数的偏导数,如果通过链式法则逐一对每个参数进行求偏导比较低效。在神经网络的训练中经常使用反向传播算法来高效地计算梯度。

4.4 反向传播算法

假设采用随机梯度下降进行神经网络参数学习,给定一个样本 (x, y),将其输入到神经网络模型中,得到网络输出为 \hat{y}。假设损失函数为 L(y,\hat{y}),要进行参数学习就需要计算损失函数关于每个参数的导数。

反向传播(Back Propagation)

4.5 自动梯度计算

实际上,参数的梯度可以让计算机来自动计算。目前,几乎所有的主流深度学习框架都包含了自动梯度计算的功能,即我们可以只考虑网络结构并用代码实现,其梯度可以自动进行计算,无需人工干预,这样可以大幅提高开发效率。

4.5.1 数值微分

数值微分(Numerical Differentiation)是用数值方法来计算函数 f(x) 的导数。函数 f(x)的点 x的导数定义为

通过上述定义来直接计算函数 f(x) 的梯度。数值微分方法非常容易实现,但找到一个合适的扰动 ∆x却十分困难。如果 ∆x过小,会引起数值计算问题,比如舍入误差;如果 ∆x过大,会增加截断误差,使得导数计算不准确。因此,数值微分的实用性比较差。

在实际应用,经常使用下面公式来计算梯度,可以减少截断误差。

数值微分的另外一个问题是计算复杂度。假设参数数量为 n,则每个参数都需要单独施加扰动,并计算梯度。假设每次正向传播的计算复杂度为 O(n),则计算数值微分的总体时间复杂度为 O(n2)。

4.5.2 符号微分

符号微分(Symbolic Differentiation)是一种基于符号计算的自动求导方法。符号计算,也叫代数计算,是指用计算机来处理带有变量的数学表达式。

这里的变量看作是符号(Symbols),一般不需要代入具体的值。符号计算的输入和输出都是数学表达式,一般包括对数学表达式的化简、因式分解、微分、积分、解代数方程、求解常微分方程等运算。

符号计算一般来讲是对输入的表达式,通过迭代或递归使用一些事先定义的规则进行转换。当转换结果不能再继续使用变换规则时,便停止计算。
符号微分可以在编译时就计算梯度的数学表示,并进一步利用符号计算方法进行优化。此外,符号计算的一个优点是符号计算和平台无关,可以在 CPU或GPU上运行。

符号微分也有一些不足之处。一是编译时间较长,特别是对于循环,需要很长时间进行编译;二是为了进行符号微分,一般需要设计一种专门的语言来表示数学表达式,并且要对变量(符号)进行预先声明;三是很难对程序进行调试

4.5.3 自动微分

自动微分(Automatic Differentiation, AD)是一种可以对一个(程序)函数进行计算导数的方法。符号微分的处理对象是数学表达式,而自动微分的处理对象是一个函数或一段程序

自动微分的基本原理是所有的数值计算可以分解为一些基本操作,包含+, -, ×, /和一些初等函数 exp, log, sin, cos等,然后利用链式法则来自动计算一个复合函数的梯度。

为了简单起见,这里以一个神经网络中常见的复合函数的例子来说明自动微分的过程。令复合函数f(x; w, b)
f(x; w, b) = {\frac{1}{e^{-(wx+b)}+1}}
首先,我们将复合函数f(x; w, b)分解为一系列的基本操作,并构成一个计算图(Computational Graph)

计算图是数学运算的图形化表示。计算图中的每个非叶子节点表示一个基本操作,每个叶子节点为一个输入变量或常量。下面给出了当 x = 1, w = 0, b = 0 时复合函数f(x; w, b)的计算图,其中连边上的红色数字表示前向计算时复合函数中每个变量的实际取值。

从计算图上可以看出,复合函数f(x; w, b)由 6 个基本函数 h_i, 1 ≤ i ≤ 6 组成。每个基本函数的导数都十分简单,可以通过规则来实现。
整个复合函数 f(x; w, b)关于参数 w​ 和 ​b 的导数可以通过计算图上的节点f(x; w, b)与参数 w 和 b之间路径上所有的导数连乘来得到,即

如果函数和参数之间有多条路径,可以将这多条路径上的导数再进行相加,得到最终的梯度。

按照计算导数的顺序,自动微分可以分为两种模式:前向模式和反向模式。

前向模式 前向模式是按计算图中计算方向的相同方向来递归地计算梯度。 当 x = 1, w = 0, b = 0 时,前向模式的累积计算顺序如下:

反向模式 反向模式是按计算图中计算方向的相反方向来递归地计算梯度。 当x = 1, w = 0, b = 0 时,反向模式的累积计算顺序如下:

前向模式和反向模式可以看作是应用链式法则的两种梯度累积方式。从反向模式的计算顺序可以看出,反向模式和反向传播的计算梯度的方式相同

符号微分和自动微分

符号微分和自动微分都利用计算图和链式法则来自动求解导数。

符号微分在编译阶段先构造一个复合函数的计算图,通过符号计算得到导数的表达式,还可以对导数表达式进行优化,在程序运行阶段才代入变量的具体数值进行计算导数。

自动微分则无需事先编译,在程序运行阶段边计算边记录计算图,计算图上的局部梯度都直接代入数值进行计算,然后用前向或反向模式来计算最终的梯度。

符号微分与自动微分对比

静态计算图和动态计算图

静态计算图是在编译时构建计算图,计算图构建好之后在程序运行时不能改变。

动态计算图是在程序运行时动态构建。

两种构建方式各有优缺点。

静态计算图在构建时可以进行优化,并行能力强,但灵活性比较差

动态计算图则不容易优化,当不同输入的网络结构不一致时,难以并行计算,但是灵活性比较高

在目前深度学习框架里, Theano 和 Tensorflow 采用的是静态计算图,而DyNet,Chainer和 PyTorch采用的是动态计算图。

4.6 优化问题

4.6.1 非凸优化问题

神经网络的优化问题是一个非凸优化问题。

4.6.2 梯度消失问题

由于 Sigmoid 型函数的饱和性,饱和区的导数更是接近于 0。这样,误差经过每一层传递都会不断衰减。当网络层数很深时,梯度就会不停的衰减,甚至消失,使得整个网络很难训练。这就是所谓的梯度消失问题(Vanishing Gradient Problem),也称为梯度弥散问题。

在深层神经网络中,减轻梯度消失问题的方法有很多种。一种简单有效的方式是使用导数比较大的激活函数,比如 ReLU等。

4.7 总结和深入阅读

常见激活函数及其导数
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348

推荐阅读更多精彩内容