生成对抗网络-理论部分|深度学习(李宏毅)(二十三)

视频地址:
①B站:https://www.bilibili.com/video/BV15W411i7uP
②油管:https://www.youtube.com/watch?v=0CKeqXl5IY0

之前的博客地址:生成对抗网络-基本概念|深度学习(李宏毅)(二十二)

一、极大似然估计

  1. 极大似然估计

在GAN中,对于真实的训练样本的分布,记作P_{data}(x),这个分布也就是GAN试图去拟合、逼近的分布。另外有一个由参数\theta控制的分布记作P_{G}(x;\theta ),其实也就是GAN或者说Generator生成的对象的分布。简单来说我们的目标就是让P_{G}(x;\theta )P_{data}(x)越接近越好。

举例来说,P_{G}(x;\theta )可以是一个高斯混合模型(Gaussian Maxture Model,GMM),那么此时参数\theta就是GMM中高斯分布的均值和方差或者其他参数,我们的目的也就是求解最优的参数\theta来让P_{G}(x;\theta )尽可能地接近P_{data}(x)。从P_{data}(x)中采样\left \{x^{1},x^{2},\cdots ,x^{m}\right \},对于x^i我们可以计算P_{G}(x^{i};\theta ),然后就可以计算样本的似然:

L=\prod_{i=1}^{m}P_{G}(x^{i};\theta )

寻找最优化的参数\theta ^{*}的方法也就是极大似然估计的方法,即寻找一个参数\theta ^{*}来最大化L。我们可以对这个过程做以下变换:

\theta ^{*}=\underset{\theta }{argmax}\prod_{i=1}^{m}P_{G}(x^{i};\theta )\\ =\underset{\theta }{argmax}\; log\prod_{i=1}^{m}P_{G}(x^{i};\theta )\\ =\underset{\theta }{argmax}\sum_{i=1}^{m}logP_{G}(x^{i};\theta )\\ \approx \underset{\theta }{argmax}E_{x\sim P_{data}}[logP_{G}(x^{i};\theta )]\\ =\underset{\theta }{argmax}\int _{x}P_{data}(x)logP_{G}(x;\theta )\mathrm{d}x\\ =\underset{\theta }{argmax}(\int _{x}P_{data}(x)logP_{G}(x;\theta )\mathrm{d}x-\int _{x}P_{data}(x)logP_{data}(x)\mathrm{d}x)\\ =\underset{\theta }{argmin}\; KL(P_{data}(x)||P_{G}(x;\theta ))

上面的式子中,由于\left \{x^{1},x^{2},\cdots ,x^{m}\right \}是从P_{data}(x)采样得到的样本,因此似然就近似于上式中关于P_{data}(x)的期望值,这个期望值写成积分的形式后可以减去一个与\theta无关的项,最终得到P_{data}(x)P_{G}(x;\theta )的KL散度。也就是说,求解参数\theta的过程,也就是最小化分布P_{data}(x)P_{G}(x;\theta )的KL散度的过程,使得这两个分布能够不断地逼近。KL散度能够用来衡量两个分布的接近程度(越小越接近),其公式如下:

KL(P(x)||Q(x))=\int _{x} P(x)log\frac{P(x)}{Q(x)}\mathrm{d}x

  1. 存在的问题

使用GMM的话会限制模型的拟合能力,而对于要拟合的分布P_{data}(x)来说,其往往是图片、文字一类复杂的结构,因此我们期待使用一个神经网络,也就是一个比较复杂的、拟合能力较强的、一般化的P_{G}(x;\theta )。现在我们尝试使用一个神经网络来替代GMM,现在的\theta也就相当于神经网络的参数,具体地,我们有一个Generator,它的输入是一个随机变量zz服从高斯分布或者均匀分布P_{prior}(z),而它的输出就是G(z)=x,可以理解为生成的图片。也就是说现在我们的目标就是学习一个最优化的Generator的参数\theta,来让z通过Generator产生的x的分布P_{G}(x;\theta )P_{data}(x)越接近越好:

Generator

由于z是一个服从高斯分布或者均匀分布的随机变量,因此通过Generator以后得到的x也会服从一个分布,虽然z服从一个简单的分布,但是由于神经网络可以是很庞大的,因此x的分布可以是很复杂的。计算x出现的概率可以使用以下公式:

P_{G}(x)=\int _{z}P_{prior}(z)I_{[G(z)=x]}\mathrm{d}z

对于上面的概率公式,这个概率是很难计算的,显然不能用它来做极大似然估计,因此问题也就出在这里。类比GMM,在GMM中生成的过程是先从几个高斯分布中按照一定概率来抽取一个高斯分布,然后从这个高斯分布中抽取一个x,而在Generator中生成的过程是从P_{prior}(z)抽取一个z,然后z通过G(z)得到x,不一样的是对于一个给定的x,GMM可以轻易地计算x出现的概率,而Generator的对于一个给定的x很难计算其概率,并且概率公式中还包含指示函数,使得进行极大似然估计时没办法做微分,也就出现了问题,而GAN的优势就在于它解决了这个问题,这就是GAN最大的贡献。

二、GAN的原理

  1. GAN的基本理念

在GAN中Generator是一个函数G,输入是z,输出是x,给定一个z的分布P_{prior}(z),函数G也就定义了分布P_{G}(x),然而单纯利用Generator无法使用极大似然估计。另外有一个Discriminator记作函数D,它的输入是x,输出是一个标量,它能够衡量P_{G}(x)P_{data}(x)之间的差异,不过它计算的不是KL散度,而是另一种散度。如何利用GAN来求解最优化的Generator呢?只需要求解下面这个式子:

G^{*}=\underset{G}{argmin}\; \underset{D}{max}\; V(G,D)

直观地来看,如下图,对于上面的式子,如果对于特定的G,比如G_1G_2G_3,令V(G,D)最大的D就是红点对应的D,然后求解使\underset{D}{max}\; V(G,D)最小的G,就能解得最优化的G_3

求解

V也就是目标函数,可以使用这个式子:

V=E_{x\sim P_{data}}[logD(x)]+E_{x\sim P_{G}}[log(1-D(x))]

对于一个给定的G\underset{D}{max}\; V(G,D)的值就相当于P_{G}(x)P_{data}(x)之间差异的程度,其实这个值就等于P_{G}(x)P_{data}(x)的某种散度,然后再求解一个能使\underset{D}{max}\; V(G,D)最小的G,就能找到一个能使P_{G}(x)P_{data}(x)最接近的G了。

  1. GAN的原理

接下来就解释一下为什么按照上面的方式求解就能得到最优化的结果。首先对于一个给定的G,寻找需要寻找一个D^*来使V(G,D)最大,我们先来将V(G,D)的式子展开:

V=E_{x\sim P_{data}}[logD(x)]+E_{x\sim P_{G}}[log(1-D(x))]\\ =\int _{x}P_{data}(x)logD(x)\mathrm{d}x+\int _{x}P_{G}(x)log(1-D(x))\mathrm{d}x\\ =\int _{x}[P_{data}(x)logD(x)+P_{G}(x)log(1-D(x))]\mathrm{d}x

对于上面的式子,我们想让V(G,D)最大,自然希望对于每一个x,积分号里面的式子都能最大,因此我们只看积分号里面的部分:

P_{data}(x)logD(x)+P_{G}(x)log(1-D(x))

上面的式子中,P_{data}(x)作为客观存在的分布,因此看做常数,而P_{G}(x)由于是给定的,因此也看做常数,而D(x)作为变量,也就是:

\underset{a}{P_{data}(x)}log\underset{D}{D(x)}+\underset{b}{P_{G}(x)}log(1-\underset{D}{D(x)})

那这个问题就成了求解D^*来使得下面这个式子最大的问题:

f(D)=alogD+blog(1-D)

直接微分然后令其等于0即可:

\frac{\mathrm{d}f(D)}{\mathrm{d}D}=a\times \frac{1}{D}-b\times\frac{1}{1-D}\\ \Rightarrow a\times \frac{1}{D^{*}}-b\times\frac{1}{1-D^{*}}=0\\ \Rightarrow D^{*}=\frac{a}{a+b}

也就是说:

D^{*}(x)=\frac{P_{data}(x)}{P_{data}(x)+P_{G}(x)}

最优的D^{*}(x)显然位于01之间,因此在实际实现GAN时可以给Discriminator最后设置一个sigmoid函数。

在下图中,不同的G的图像的最高点对应的D(x)就对应着由上面的式子解出来的D^*(x),而将D^*(x)代入V(G,D)中得到的V(G,D^*)就表示了在给定的当前G的情况下P_{G}(x)P_{data}(x)的某种散度,在图中也就是红点到横轴的距离:

代入

接下来说明为什么\underset{D}{max}\; V(G,D)能够代表P_{G}(x)P_{data}(x)之间的差异程度。将D^{*}(x)代入V(G,D)中并做一些变换:

\underset{D}{max}\; V(G,D)=V(G,D^{*})\\ =E_{x\sim P_{data}}[log\frac{P_{data}(x)}{P_{data}(x)+P_{G}(x)}]+E_{x\sim P_{G}}[log\frac{P_{G}(x)}{P_{data}(x)+P_{G}(x)}]\\ =\int _{x}P_{data}(x)log\frac{P_{data}(x)}{P_{data}(x)+P_{G}(x)}\mathrm{d}x+\int _{x}P_{G}(x)log\frac{P_{G}(x)}{P_{data}(x)+P_{G}(x)}\mathrm{d}x\\ =\int _{x}P_{data}(x)log\frac{P_{data}(x){\color{Red}{/2}}}{(P_{data}(x)+P_{G}(x)){\color{Red}{/2}}}\mathrm{d}x+\int _{x}P_{G}(x)log\frac{P_{G}(x){\color{Red}{/2}}}{(P_{data}(x)+P_{G}(x)){\color{Red}{/2}}}\mathrm{d}x\\ =\int _{x}P_{data}(x)\left ({\color{Red}{log\frac{1}{2}}}+log\frac{P_{data}(x)}{(P_{data}(x)+P_{G}(x)){\color{Red}{/2}}}\right )\mathrm{d}x+\int _{x}P_{G}(x)\left ({\color{Red}{log\frac{1}{2}}}+log\frac{P_{G}(x)}{(P_{data}(x)+P_{G}(x)){\color{Red}{/2}}}\right )\mathrm{d}x\\ ={\color{Red}{-2log2}}+\int _{x}P_{data}(x)log\frac{P_{data}(x)}{(P_{data}(x)+P_{G}(x))/2}\mathrm{d}x+\int _{x}P_{G}(x)log\frac{P_{G}(x)}{(P_{data}(x)+P_{G}(x))/2}\mathrm{d}x\\ =-2log2+{\color{Red}{KL\left (P_{data}(x)||\frac{P_{data}(x)+P_{G}(x)}{2} \right )}}+{\color{Red}{KL\left (P_{G}(x)||\frac{P_{data}(x)+P_{G}(x)}{2} \right )}}\\ =-2log2+{\color{Red}{2JS(P_{data}(x)||P_{G}(x))}}

这里我们定义另一种衡量分布差异程度的散度,叫做JS散度(Jensen–Shannon Divergence,JSD)。不同于KL散度的是,JS散度是对称的,也就是JS(P||Q)=JS(Q||P)。它的定义如下:

JS(P(x)||Q(x))=\frac{1}{2}KL(P(x)||M(x))+\frac{1}{2}KL(Q(x)||M(x)),\; 其中M(x)=\frac{1}{2}(P(x)+Q(x))

因此将\underset{D}{max}\; V(G,D)也就相当于在衡量P_{G}(x)P_{data}(x)的JS散度,JS散度的值介于0log2之间,如果两个分布完全一致那么其JS散度就是0,如果两个分布完全没有交集,那么其JS散度就是log2。当然也可以识别别的V来让它衡量别的散度。

总结一下:

①首先我们有一个GeneratorG和一个DiscriminatorD
②我们需要通过下面这一个式子寻找一个G^*
G^{*}=\underset{G}{argmin}\; \underset{D}{max}\; V(G,D)
③对于给定的G,有:
\underset{D}{max}\; V(G,D)=-2log2+2JS(P_{data}(x)||P_{G}(x))
④然后需要求解最优的G,满足P_{G}(x)=P_{data}(x)时才会得到最小的V(G,D)

  1. 算法

接下来要做的就是求解求解G^*,我们把\underset{D}{max}\; V(G,D)记作L(G),那么求解G^*的方法按照梯度下降的方法就好:

\theta _{G}\leftarrow \theta _{G}-\eta \frac{\partial L(G)}{\partial \theta _{G}}

那么对于L(G)这样这种带max的式子要怎么做微分呢?我们可以把带max的问题以下面这个问题来看,对于函数f(x),其公式为:

f(x)=max\left \{f_{1}(x),f_{2}(x),f_{3}(x)\right \}

现在要求解\frac{\mathrm{d}f(x)}{\mathrm{d}x},那么假设f(x)的图像如下:

f(x)

那么在求解时x所在的位置对应的哪一个f_{i}(x)最大,那么对x的微分也就是\frac{\mathrm{d}f_{i}(x)}{\mathrm{d}x}

微分

在求解这个问题时就按照对当前x对应的最大的f_i(x)微分的方式进行梯度下降即可:

梯度下降

上面的问题类比到求解G^*上就是f(x)相当于L(G)x相当于Gf_i(x)也就相当于不同的D,只不过f_i(x)是有限个,D有无限多个,不同的G也就对应了不同的能使V最大的D^*。因此我们的求解方式是按照梯度下降的方法来求解G^*,每次更新\theta _{G}以后要计算当前G对应的D^*,然后再一次地更新\theta _{G}。该流程如下:

  • 初始化G_0
  • 求解D_0^*来最大化V(G_0,D)V(G_0,D_0^*)就是P_{G_0}(x)P_{data}(x)的JS散度
  • 更新\theta _{G}来获得G_1
    \theta _{G}\leftarrow \theta _{G}-\eta \frac{\partial V(G_{0},D_0^{*})}{\partial \theta _{G}}
  • 求解D_1^*来最大化V(G_1,D)V(G_0,D_1^*)就是P_{G_1}(x)P_{data}(x)的JS散度
  • 更新\theta _{G}来获得G_2
    \theta _{G}\leftarrow \theta _{G}-\eta \frac{\partial V(G_{0},D_1^{*})}{\partial \theta _{G}}
  • ……

这里有个小问题就是在使用梯度下降更新过G以后可能会使得P_{G}(x)P_{data}(x)的JS散度不减反增,比如下图这个例子,横轴表示D,在更新过G的参数后有可能\underset{D}{max}\; V(G,D)会比原来更大,对于这样的问题我们就只能假设D_0^*\approx D_1^*,在更新参数\theta _G时一次不能更新太多:

issue

在实际操作的时候因为无法积分所以我们并不能真正地计算V中的两个期望,因此采用采样的方法。对于给定的G,求解使V(G,D)最大化的D^*时,从P_{data}(x)中采样\left \{x^{1},x^{2},\cdots ,x^{m}\right \},从GeneratorP_{G}(x)中采样\left \{\tilde{x}^{1},\tilde{x}^{2},\cdots ,\tilde{x}^{m}\right \},然后最大化:

\tilde{V}=\frac{1}{m}\sum_{i=1}^{m}log\; D(x^{i})+\frac{1}{m}\sum_{i=1}^{m}log\; \left (1-D(\tilde{x}^{i})\right )

上面这个式子非常类似与一个二分类器的损失函数,也就是二分类的交叉熵,在二分类中,如果x是个positive的样本,我们要尽可能地极小化-logD(x),如果x是个negative的样本,我们要尽可能地极小化-log(1-D(x))。因此极大化\tilde{V}也就相当于二分类问题中极小化交叉熵损失函数,也就是说,我们真正在求解D^*时只需要当做一个二分类问题来做就好了,具体地:

\left \{x^{1},x^{2},\cdots ,x^{m}\right \}\; from\; P_{data}(x)\Rightarrow positive\; examples\\ \left \{\tilde{x}^{1},\tilde{x}^{2},\cdots ,\tilde{x}^{m}\right \}\; from\; P_{G}(x)\Rightarrow negative\; examples\\ loss function:L=-\tilde{V}=-\left (\frac{1}{m}\sum_{i=1}^{m}log\; D(x^{i})+\frac{1}{m}\sum_{i=1}^{m}log\; \left (1-D(\tilde{x}^{i})\right )\right )

这一点直观上也是可以理解的,如果这个二分类器的loss很小,就代表它可以很容易地分辨真实的样本和生成的样本,P_{G}(x)P_{data}(x)的JS散度就很大,而如果这个二分类器的loss很大,就代表它分辨不出真实的样本和生成的样本,P_{G}(x)P_{data}(x)的JS散度就很小。

现在我们就可以更加清晰地来理解上一篇文章中的训练的算法:

Initialize:
  初始化D的参数\theta _{d}G的参数\theta _{g}


Step1 学习D
  ①从数据库中随机抽样m个样本\left \{x^{1},x^{2},\cdots ,x^{m}\right \}
  ②从一个分布(比如高斯分布或者均匀分布)中采样m个噪声样本(noise sample)\left \{z^{1},z^{2},\cdots ,z^{m}\right \}
  ③获得G生成的数据\left \{\tilde{x}^{1},\tilde{x}^{2},\cdots ,\tilde{x}^{m}\right \},其中\tilde{x}^{i}=G(z^{i})
  ④目标函数记作\tilde{V},通过最大化(梯度上升)\tilde{V}来更新\theta _{d},也就是\theta _{d}\leftarrow \theta _{d}+\eta \nabla \tilde{V}(\theta _{d}),目标函数\tilde{V}为:
\tilde{V}=\frac{1}{m}\sum_{i=1}^{m}log\; D(x^{i})+\frac{1}{m}\sum_{i=1}^{m}log\; (1-D(\tilde{x}^{i}))


Step2 学习G
  ①同样从一个分布(比如高斯分布或者均匀分布)中采样m个噪声样本(noise sample)\left \{z^{1},z^{2},\cdots ,z^{m}\right \}
  ②目标函数同样记作\tilde{V},通过最小化(梯度下降)\tilde{V}来更新\theta _{g},也就是\theta _{g}\leftarrow \theta _{g}-\eta \nabla \tilde{V}(\theta _{g}),目标函数\tilde{V}为:
\tilde{V}=\frac{1}{m}\sum_{i=1}^{m}log\left (1-D(G(z^{i}))\right )

上面的算法中Step1和Step2是交替进行的,但是在每一次迭代中应该将学习D的步骤重复多次,不过即使这样也不能学习到D的全局最优点,学习到的也只是D的lower bound,而对于学习G的步骤只需要进行一次,这是因为之前说过的原因,即G不能一次更新太多。

三、实践中的一些issue

  1. 实作中Generator目标函数的问题

在训练Generator时,我们实际上在极小化这个式子:

V=E_{x\sim P_{G}}[log(1-D(x))]

D(x)为横轴,画出log(1-D(x))-logD(x)的图像如图所示:

图像

在训练一开始,D(x)接近0,但是log(1-D(x))的梯度比较小,在趋近于1的地方log(1-D(x))的梯度反而比较大,这与我们的期待是不一致的,我们期望模型在训练初始时梯度应该大一些,在接近收敛时梯度应该小一些。因此,在实际操作中,我们真正优化的式子是:

V=E_{x\sim P_{G}}[-logD(x)]

这个式子的梯度就符合我们的期望,不过这样就不是在极小化JS散度,而是在极小化另外一个奇怪的散度。

  1. 评估JS散度

在训练Discriminator时,理论上Discriminator的loss就代表JS散度的大小,但是在实际操作时Discriminator的loss几乎趋近于0,也就是说Discriminator总是有办法把生成的图片与真实的图片分开。举例来说,在下面的实验中,Generator采用了训练1、10、25个epoch的三种,其中训练越多epoch的Generator产生的图片越接近真实,但是从图中看到无论哪一种Generator它们的Discriminator的loss总是能够趋近于0,并且Discriminator也总能训练到100%的准确率,Discriminator的loss并不能反映JS散度的大小:

实验

另外一个例子如下,使用一个较强和一个较弱的Generator,可以看到强的Generator生成的图片已经很真实了,但是它们的Discriminator的loss缺失差不多的,这表明Discriminator的loss并没有反映JS散度:

实验

Discriminator的loss接近于0,表明JS散度最大,也就是log2P_{G}(x)P_{data}(x)完全没有交集。原因有以下两点:

  • Reason1:通过采样的方式来训练

由于我们始终没有办法直接计算损失函数中的期望,因此只能通过采样的方法来进行训练,那么有可能如下图所示,对于采样出的样本,由于Discriminator过于powerful,那么它总有办法寻找一个边界来分开样本,类似过拟合:

过拟合

解决这种问题我们考虑让Discriminator变得弱一点,要么迭代次数少一点要么加dropout,不过要将Discriminator变弱到什么程度,这又是很难把握的,而且这与我们最初的设想又出现了矛盾,Discriminator能够衡量JS散度的一个前提就是Discriminator要足够地powerful,因此这里就出现了一些矛盾。

  • Reason2:数据的本质

GAN要拟合的数据和Generator生成的数据实际上是高维空间中的流形(manifold)。拿二维空间中的一维流形来说,可能P_{G}(x)P_{data}(x)很少有交集,或者交集很少,像图中这样的数据的JS散度就会很小:

流形

我们之前有说过GAN的训练和生物进化很类似,比如下面图中生物进化出眼睛的过程,只要从左到右的进化对生物的繁衍是有利的,这个进化的过程才能持续下去:

眼睛的进化

GAN的训练也类似,比如下图中P_{G}(x)P_{data}(x)越来越接近,最终数据分布趋于一致,我们期待模型能够以这样的过程逐步迭代达到最佳效果,但是可以看出在达到最佳效果(JS散度为0)之前,每一步的JS散度都是log2,也就是说目前的GAN没有动力一致演化下去:

GAN的训练

解决这个问题的方法是可以给Discriminator的输入添加一些噪声或者给标签添加一些噪声(随机标记一些正样本为负样本,负样本为正样本),这样会使数据产生下图中的效果,因而重叠的部分就有可能变大:

加噪

不过要将加入的噪声随着训练而减弱,否则会影响机器对真实的数据分布的判断。

另一种方式是使用别的度量差异度的方式,比如WGAN这方法,这一类方法下一篇中再具体介绍。

  1. Mode Collapse

GAN还容易产生Mode Collapse的问题,以高斯分布为例,如果P_{data}(x)有两个高斯分布,而P_{G}(x)只产生了一个:

Mode Collapse

举例来说,在下面的二次元人物头像生成的图片中就有许多图片是重复的,这就是Mode Collapse的问题:

Mode Collapse

再举一个例子来说,比如要拟合的数据如下图:

真实数据

我们期待GAN能够按照下面的方式来逐步学习到数据的真实分布:

期待的结果

而实际的结果可能只会像下面这样,这就是Mode Collapse的问题:

实际的结果

出现Mode Collapse的原因可能如下图所示。在P_{data}有两个高斯分布而P_{G}只能产生一个高斯分布的情况下,对于KL散度,通过它的式子可以看出,在P_G没有值,而P_{data}有值的地方就会产生无穷大的值,因此为了让KL散度尽可能地小,P_{G}就会尽可能地覆盖所有P_{data}有值的地方,即使有些P_{data}没有值的地方被覆盖到也在所不惜。而对于Reverse KL散度来说正好相反,在P_{data}没有值,而P_{G}有值的地方就会产生无穷大的值,因此为了让Reverse KL散度小,P_{G}就不会冒险去覆盖P_{data}没有值的地方,因此就可能会固守在一个高斯分布上:

原因分析

但是因为V是可以由我们自己来设计的,因此我们可以设计目标函数V来让GAN最小化KL散度,然而Mode Collapse的问题还是存在。具体的有关Mode Collapse的问题之后再介绍,这里就不再赘述。

最后列一个Ian Goodfellow的有关GAN的Tutorial:https://arxiv.org/abs/1701.00160

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

推荐阅读更多精彩内容