LSTM(長(zhǎng)短期記憶人工神經(jīng)網(wǎng)絡(luò))實(shí)現(xiàn)

簡(jiǎn)介


長(zhǎng)短期記憶人工神經(jīng)網(wǎng)絡(luò)(Long-Short Term Memory, LSTM)是一種時(shí)間遞歸神經(jīng)網(wǎng)絡(luò)(RNN),論文首次發(fā)表于1997年。由于獨(dú)特的設(shè)計(jì)結(jié)構(gòu),LSTM適合于處理和預(yù)測(cè)時(shí)間序列中間隔和延遲非常長(zhǎng)的重要事件。

由于其結(jié)構(gòu)和RNN很相似惕稻,就是將單一的激活函數(shù)換成更為復(fù)雜的結(jié)構(gòu)。前面《RNN(循環(huán)神經(jīng)網(wǎng)絡(luò))訓(xùn)練手寫數(shù)字》的數(shù)據(jù)處理和很多代碼都有共通之處蝙叛,本文就從簡(jiǎn)??俺祠。

公式


LSTM的結(jié)構(gòu)有很多種形式,但是都大同小異借帘,主要都包含輸入門蜘渣、輸出門、遺忘門肺然。
本文實(shí)現(xiàn)的一種較為流行的結(jié)構(gòu)GRU(Gated Recurrent Unit)蔫缸。公式與結(jié)構(gòu)圖如下:

實(shí)現(xiàn)


相比較于簡(jiǎn)單的RNN網(wǎng)絡(luò),LSTM訓(xùn)練的參數(shù)更多际起,單個(gè)塊的結(jié)構(gòu)也更復(fù)雜拾碌。實(shí)現(xiàn)中,我將輸入的誤差也反傳了街望,這樣可以很方便的實(shí)現(xiàn)多層LSTM網(wǎng)絡(luò)校翔,或者與RNN/CNN網(wǎng)絡(luò)結(jié)合使用。
主體代碼如下:

//
//  MLLstm.m
//  LSTM
//
//  Created by Jiao Liu on 11/12/16.
//  Copyright ? 2016 ChangHong. All rights reserved.
//

#import "MLLstm.h"

@implementation MLLstm

#pragma mark - Inner Method

+ (double)truncated_normal:(double)mean dev:(double)stddev
{
    double outP = 0.0;
    do {
        static int hasSpare = 0;
        static double spare;
        if (hasSpare) {
            hasSpare = 0;
            outP = mean + stddev * spare;
            continue;
        }
        
        hasSpare = 1;
        static double u,v,s;
        do {
            u = (rand() / ((double) RAND_MAX)) * 2.0 - 1.0;
            v = (rand() / ((double) RAND_MAX)) * 2.0 - 1.0;
            s = u * u + v * v;
        } while ((s >= 1.0) || (s == 0.0));
        s = sqrt(-2.0 * log(s) / s);
        spare = v * s;
        outP = mean + stddev * u * s;
    } while (fabsl(outP) > 2*stddev);
    return outP;
}

+ (double *)fillVector:(double)num size:(int)size
{
    double *outP = malloc(sizeof(double) * size);
    vDSP_vfillD(&num, outP, 1, size);
    return outP;
    
}

+ (double *)weight_init:(int)size
{
    double *outP = malloc(sizeof(double) * size);
    for (int i = 0; i < size; i++) {
        outP[i] = [MLLstm truncated_normal:0 dev:0.1];
    }
    return outP;
}

+ (double *)bias_init:(int)size
{
    return [MLLstm fillVector:0.1f size:size];
}

+ (double *)tanh:(double *)input size:(int)size
{
    for (int i = 0; i < size; i++) {
        double num = input[i];
        if (num > 20) {
            input[i] = 1;
        }
        else if (num < -20)
        {
            input[i] = -1;
        }
        else
        {
            input[i] = (exp(num) - exp(-num)) / (exp(num) + exp(-num));
        }
    }
    return input;
}

+ (double *)sigmoid:(double *)input size:(int)size
{
    for (int i = 0; i < size; i++) {
        double num = input[i];
        if (num > 20) {
            input[i] = 1;
        }
        else if (num < -20)
        {
            input[i] = 0;
        }
        else
        {
            input[i] = exp(num) / (exp(num) + 1);
        }
    }
    return input;
}

#pragma mark - Init

- (id)initWithNodeNum:(int)num layerSize:(int)size dataDim:(int)dim
{
    self = [super init];
    if (self) {
        _nodeNum = num;
        _layerSize = size;
        _dataDim = dim;
        [self setupNet];
    }
    return self;
}

- (id)init
{
    self = [super init];
    if (self) {
        [self setupNet];
    }
    return self;
}

- (void)setupNet
{
    _hState = calloc(_layerSize * _nodeNum, sizeof(double));
    _rState = calloc(_layerSize * _nodeNum, sizeof(double));
    _zState = calloc(_layerSize * _nodeNum, sizeof(double));
    _hbState = calloc(_layerSize * _nodeNum, sizeof(double));
    _output = calloc(_layerSize * _dataDim, sizeof(double));
    _backLoss = calloc(_layerSize * _dataDim, sizeof(double));
    
    _rW = [MLLstm weight_init:_nodeNum * _dataDim];
    _rU = [MLLstm weight_init:_nodeNum * _nodeNum];
    _rBias = [MLLstm bias_init:_nodeNum];
    _zW = [MLLstm weight_init:_nodeNum * _dataDim];
    _zU = [MLLstm weight_init:_nodeNum * _nodeNum];
    _zBias = [MLLstm bias_init:_nodeNum];
    _hW = [MLLstm weight_init:_nodeNum * _dataDim];
    _hU = [MLLstm weight_init:_nodeNum * _nodeNum];
    _hBias = [MLLstm bias_init:_nodeNum];
    _outW = [MLLstm weight_init:_dataDim * _nodeNum];
    _outBias = [MLLstm bias_init:_dataDim];
}

- (double *)forwardPropagation:(double *)input
{
    _input = input;
    // clean data
    double zero = 0;
    vDSP_vfillD(&zero, _output, 1, _layerSize * _dataDim);
    vDSP_vfillD(&zero, _hState, 1, _layerSize * _nodeNum);
    vDSP_vfillD(&zero, _rState, 1, _layerSize * _nodeNum);
    vDSP_vfillD(&zero, _zState, 1, _layerSize * _nodeNum);
    vDSP_vfillD(&zero, _hbState, 1, _layerSize * _nodeNum);
    vDSP_vfillD(&zero, _backLoss, 1, _layerSize * _dataDim);
    
    double *temp1 = calloc(_nodeNum, sizeof(double));
    double *temp2 = calloc(_nodeNum, sizeof(double));
    double *temp3 = calloc(_nodeNum, sizeof(double));
    double *one = [MLLstm fillVector:1 size:_nodeNum];
    for (int i = 0; i < _layerSize; i++) {
        //rj =σ  [Wr*(xt)]j +  Ur*h?t?1? + rBias]
        if (i == 0) {
            vDSP_mmulD(_rW, 1, (_input + i * _dataDim), 1, temp1, 1, _nodeNum, 1, _dataDim);
            vDSP_vaddD(temp1, 1, _rBias, 1, temp1, 1, _nodeNum);
        }
        else
        {
            vDSP_mmulD(_rW, 1, (_input + i * _dataDim), 1, temp1, 1, _nodeNum, 1, _dataDim);
            vDSP_mmulD(_rU, 1, (_hState + (i-1) * _nodeNum), 1, temp2, 1, _nodeNum, 1, _nodeNum);
            vDSP_vaddD(temp1, 1, temp2, 1, temp1, 1, _nodeNum);
            vDSP_vaddD(temp1, 1, _rBias, 1, temp1, 1, _nodeNum);
        }
        [MLLstm sigmoid:temp1 size:_nodeNum];
        vDSP_vaddD((_rState + i * _nodeNum), 1, temp1, 1, (_rState + i * _nodeNum), 1, _nodeNum);
        
        //zj =σ  [Wz*(xt)]j +  Uz*h?t?1? + zBias]
        if (i == 0) {
            vDSP_mmulD(_zW, 1, (_input + i * _dataDim), 1, temp1, 1, _nodeNum, 1, _dataDim);
            vDSP_vaddD(temp1, 1, _zBias, 1, temp1, 1, _nodeNum);
        }
        else
        {
            vDSP_mmulD(_zW, 1, (_input + i * _dataDim), 1, temp1, 1, _nodeNum, 1, _dataDim);
            vDSP_mmulD(_zU, 1, (_hState + (i-1) * _nodeNum), 1, temp2, 1, _nodeNum, 1, _nodeNum);
            vDSP_vaddD(temp1, 1, temp2, 1, temp1, 1, _nodeNum);
            vDSP_vaddD(temp1, 1, _zBias, 1, temp1, 1, _nodeNum);
        }
        [MLLstm sigmoid:temp1 size:_nodeNum];
        vDSP_vaddD((_zState + i * _nodeNum), 1, temp1, 1, (_zState + i * _nodeNum), 1, _nodeNum);
        
        //h ??t? = tanh  {[W*(xt)] +  U * [r ⊙ h?t?1?] + hBias}
        if (i == 0) {
            vDSP_mmulD(_hW, 1, (_input + i * _dataDim), 1, temp1, 1, _nodeNum, 1, _dataDim);
            vDSP_vaddD(temp1, 1, _hBias, 1, temp1, 1, _nodeNum);
        }
        else
        {
            vDSP_mmulD(_hW, 1, (_input + i * _dataDim), 1, temp1, 1, _nodeNum, 1, _dataDim);
            vDSP_vmulD((_rState + i * _nodeNum), 1, (_hState + (i-1) * _nodeNum), 1, temp2, 1, _nodeNum);
            vDSP_mmulD(_hU, 1, temp2, 1, temp3, 1, _nodeNum, 1, _nodeNum);
            vDSP_vaddD(temp1, 1, temp3, 1, temp1, 1, _nodeNum);
            vDSP_vaddD(temp1, 1, _hBias, 1, temp1, 1, _nodeNum);
        }
        [MLLstm tanh:temp1 size:_nodeNum];
        vDSP_vaddD((_hbState + i * _nodeNum), 1, temp1, 1, (_hbState + i * _nodeNum), 1, _nodeNum);
        
        //h?t? = zj⊙ h?t?1? + (1 ? zj)⊙ h ??t?
        if (i == 0) {
            vDSP_vsubD((_zState + i * _nodeNum), 1, one, 1, temp1, 1, _nodeNum);
            vDSP_vmulD((_hbState + i * _nodeNum), 1, temp1, 1, temp1, 1, _nodeNum);
        }
        else
        {
            vDSP_vsubD((_zState + i * _nodeNum), 1, one, 1, temp1, 1, _nodeNum);
            vDSP_vmulD((_hbState + i * _nodeNum), 1, temp1, 1, temp1, 1, _nodeNum);
            vDSP_vmulD((_zState + i * _nodeNum), 1, (_hState + (i-1) * _nodeNum), 1, temp2, 1, _nodeNum);
            vDSP_vaddD(temp1, 1, temp2, 1, temp1, 1, _nodeNum);
        }
        vDSP_vaddD((_hState + i * _nodeNum), 1, temp1, 1, (_hState + i * _nodeNum), 1, _nodeNum);
        
        // output
        vDSP_mmulD(_outW, 1, (_hState + i * _nodeNum), 1, (_output + i * _dataDim), 1, _dataDim, 1, _nodeNum);
        vDSP_vaddD(_outBias, 1, (_output + i * _dataDim), 1, (_output + i * _dataDim), 1, _dataDim);
    }
    free(one);
    free(temp1);
    free(temp2);
    free(temp3);
    
    return _output;
}

- (double *)backPropagation:(double *)loss
{
    double *flowLoss = calloc(_nodeNum, sizeof(double));
    double *outTW = calloc(_nodeNum * _dataDim, sizeof(double));
    double *outLoss = calloc(_nodeNum, sizeof(double));
    double *outWLoss = calloc(_dataDim * _nodeNum, sizeof(double));
    double *temp1 = calloc(_nodeNum, sizeof(double));
    double *one = [MLLstm fillVector:1 size:_nodeNum];
    double *zLoss = calloc(_nodeNum, sizeof(double));
    double *hbLoss = calloc(_nodeNum, sizeof(double));
    double *inWLoss = calloc(_nodeNum * _dataDim, sizeof(double));
    double *rLoss = calloc(_nodeNum, sizeof(double));
    double *tU = calloc(_nodeNum * _nodeNum, sizeof(double));
    double *uLoss = calloc(_nodeNum * _nodeNum, sizeof(double));
    double *tW = calloc(_dataDim * _nodeNum, sizeof(double));
    double *temp2 = calloc(_dataDim, sizeof(double));
    for (int i = _layerSize - 1; i >= 0; i--) {
        // update output parameters
        vDSP_vaddD(_outBias, 1, (loss + i * _dataDim), 1, _outBias, 1, _dataDim);
        vDSP_mtransD(_outW, 1, outTW, 1, _nodeNum, _dataDim);
        vDSP_mmulD(outTW, 1, (loss + i * _dataDim), 1, outLoss, 1, _nodeNum, 1, _dataDim);
        vDSP_mmulD((loss + i * _dataDim), 1, (_hState + i * _nodeNum), 1, outWLoss, 1, _dataDim, _nodeNum, 1);
        vDSP_vaddD(_outW, 1, outWLoss, 1, _outW, 1, _dataDim * _nodeNum);
        
        // h(t) back loss
        if (i != _layerSize - 1) {
            vDSP_vaddD(outLoss, 1, flowLoss, 1, outLoss, 1, _nodeNum);
        }
        if (i > 0) {
            vDSP_vsubD((_hState + (i-1) * _nodeNum), 1, (_hbState + i * _nodeNum), 1, temp1, 1, _nodeNum);
            vDSP_vmulD(outLoss, 1, temp1, 1, zLoss, 1, _nodeNum);
            
            vDSP_vsubD((_zState + i * _nodeNum), 1, one, 1, temp1, 1, _nodeNum);
            vDSP_vmulD(outLoss, 1, temp1, 1, flowLoss, 1, _nodeNum);
        }
        else
        {
            vDSP_vmulD(outLoss, 1, (_hbState + i * _nodeNum), 1, zLoss, 1, _nodeNum);
        }
        // σ` = f(x)*(1-f(x))
        vDSP_vsubD((_zState + i * _nodeNum), 1, one, 1, temp1, 1, _nodeNum);
        vDSP_vmulD(temp1, 1, (_zState + i * _nodeNum), 1, temp1, 1, _nodeNum);
        vDSP_vmulD(temp1, 1, zLoss, 1, zLoss, 1, _nodeNum);
        
        vDSP_vmulD(outLoss, 1, (_zState + i * _nodeNum), 1, hbLoss, 1, _nodeNum);
        // tanh` =  1-f(x)**2
        vDSP_vsqD((_hbState + i * _nodeNum), 1, temp1, 1, _nodeNum);
        vDSP_vsubD(temp1, 1, one, 1, temp1, 1, _nodeNum);
        vDSP_vmulD(hbLoss, 1, temp1, 1, hbLoss, 1, _nodeNum);
        
        // update h`(t) parameters
        vDSP_vaddD(_hBias, 1, hbLoss, 1, _hBias, 1, _nodeNum);
        vDSP_mtransD(_hW, 1, tW, 1, _dataDim, _nodeNum);
        vDSP_mmulD(tW, 1, hbLoss, 1, temp2, 1, _dataDim, 1, _nodeNum);
        vDSP_vaddD((_backLoss + i * _dataDim), 1, temp2, 1, (_backLoss + i * _dataDim), 1, _dataDim);
        vDSP_mmulD(hbLoss, 1, (_input + i * _dataDim), 1, inWLoss, 1, _nodeNum, _dataDim, 1);
        vDSP_vaddD(_hW, 1, inWLoss, 1, _hW, 1, _nodeNum * _dataDim);

        if (i > 0) {
            vDSP_mtransD(_hU, 1, tU, 1, _nodeNum, _nodeNum);
            vDSP_mmulD(tU, 1, hbLoss, 1, rLoss, 1, _nodeNum, 1, _nodeNum);
            vDSP_vmulD(rLoss, 1, (_hState + (i-1) * _nodeNum), 1, rLoss, 1, _nodeNum);
            vDSP_vsubD((_rState + i * _nodeNum), 1, one, 1, temp1, 1, _nodeNum);
            vDSP_vmulD(temp1, 1, (_rState + i * _nodeNum), 1, temp1, 1, _nodeNum);
            vDSP_vmulD(temp1, 1, rLoss, 1, rLoss, 1, _nodeNum);
            
            vDSP_mmulD(tU, 1, hbLoss, 1, temp1, 1, _nodeNum, 1, _nodeNum);
            vDSP_vmulD(temp1, 1, (_rState + i * _nodeNum), 1, temp1, 1, _nodeNum);
            vDSP_vaddD(flowLoss, 1, temp1, 1, flowLoss, 1, _nodeNum);
            
            vDSP_vmulD((_rState + i * _nodeNum), 1, (_hState + (i-1) * _nodeNum), 1, temp1, 1, _nodeNum);
            vDSP_mmulD(hbLoss, 1, temp1, 1, uLoss, 1, _nodeNum, _nodeNum, 1);
            vDSP_vaddD(_hU, 1, uLoss, 1, _hU, 1, _nodeNum * _nodeNum);
        }
        
        // update z(t) parameters
        vDSP_vaddD(_zBias, 1, zLoss, 1, _zBias, 1, _nodeNum);
        vDSP_mtransD(_zW, 1, tW, 1, _dataDim, _nodeNum);
        vDSP_mmulD(tW, 1, zLoss, 1, temp2, 1, _dataDim, 1, _nodeNum);
        vDSP_vaddD((_backLoss + i * _dataDim), 1, temp2, 1, (_backLoss + i * _dataDim), 1, _dataDim);
        vDSP_mmulD(zLoss, 1, (_input + i * _dataDim), 1, inWLoss, 1, _nodeNum, _dataDim, 1);
        vDSP_vaddD(_zW, 1, inWLoss, 1, _zW, 1, _nodeNum * _dataDim);
        
        if (i > 0) {
            vDSP_mtransD(_zU, 1, tU, 1, _nodeNum, _nodeNum);
            vDSP_mmulD(tU, 1, zLoss, 1, temp1, 1, _nodeNum, 1, _nodeNum);
            vDSP_vaddD(flowLoss, 1, temp1, 1, flowLoss, 1, _nodeNum);
            
            vDSP_mmulD(zLoss, 1, (_hState + (i-1) * _nodeNum), 1, uLoss, 1, _nodeNum, _nodeNum, 1);
            vDSP_vaddD(_zU, 1, uLoss, 1, _zU, 1, _nodeNum * _nodeNum);
        }
        
        // update r(t) parameters
        if (i > 0) {
            vDSP_vaddD(_rBias, 1, rLoss, 1, _rBias, 1, _nodeNum);
            vDSP_mtransD(_rW, 1, tW, 1, _dataDim, _nodeNum);
            vDSP_mmulD(tW, 1,rLoss, 1, temp2, 1, _dataDim, 1, _nodeNum);
            vDSP_vaddD((_backLoss + i * _dataDim), 1, temp2, 1, (_backLoss + i * _dataDim), 1, _dataDim);
            vDSP_mmulD(rLoss, 1, (_input + i * _dataDim), 1, inWLoss, 1, _nodeNum, _dataDim, 1);
            vDSP_vaddD(_rW, 1, inWLoss, 1, _rW, 1, _nodeNum * _dataDim);
            
            vDSP_mtransD(_rU, 1, tU, 1, _nodeNum, _nodeNum);
            vDSP_mmulD(tU, 1, rLoss, 1, temp1, 1, _nodeNum, 1, _nodeNum);
            vDSP_vaddD(flowLoss, 1, temp1, 1, flowLoss, 1, _nodeNum);
            
            vDSP_mmulD(rLoss, 1, (_hState + (i-1) * _nodeNum), 1, uLoss, 1, _nodeNum, _nodeNum, 1);
            vDSP_vaddD(_rU, 1, uLoss, 1, _rU, 1, _nodeNum * _nodeNum);
        }
    }
    
    free(flowLoss);
    free(outTW);
    free(outLoss);
    free(outWLoss);
    free(temp1);
    free(one);
    free(zLoss);
    free(hbLoss);
    free(inWLoss);
    free(rLoss);
    free(tU);
    free(uLoss);
    free(tW);
    free(temp2);
    return _backLoss;
}

@end

結(jié)語(yǔ)


這里同樣用MNIST數(shù)據(jù)訓(xùn)練了單層LSTM的效果它匕,參數(shù)選用單個(gè)神經(jīng)元節(jié)點(diǎn)500展融,迭代1300窖认,一次5張圖片豫柬,得到90%左右正確率。

多次嘗試發(fā)現(xiàn)神經(jīng)元節(jié)點(diǎn)個(gè)數(shù)越大扑浸,單次迭代訓(xùn)練時(shí)間越長(zhǎng)烧给,準(zhǔn)確率越高。所以將節(jié)點(diǎn)個(gè)數(shù)設(shè)到500喝噪,為了加快速度將一次迭代圖片數(shù)由RNN網(wǎng)絡(luò)的100降到5張础嫡,但是整個(gè)過程還是花了3個(gè)多小時(shí)??。其效果不及CNN酝惧、RNN在相似環(huán)境下的表現(xiàn)榴鼎。

有興趣的朋友可以點(diǎn)這里看完整代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末晚唇,一起剝皮案震驚了整個(gè)濱河市巫财,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哩陕,老刑警劉巖平项,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赫舒,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡闽瓢,警方通過查閱死者的電腦和手機(jī)接癌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)扣讼,“玉大人缺猛,你說(shuō)我怎么就攤上這事⊥址” “怎么了枯夜?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)艰山。 經(jīng)常有香客問我湖雹,道長(zhǎng),這世上最難降的妖魔是什么曙搬? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任摔吏,我火速辦了婚禮,結(jié)果婚禮上纵装,老公的妹妹穿的比我還像新娘征讲。我一直安慰自己,他們只是感情好橡娄,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布诗箍。 她就那樣靜靜地躺著,像睡著了一般挽唉。 火紅的嫁衣襯著肌膚如雪滤祖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天瓶籽,我揣著相機(jī)與錄音匠童,去河邊找鬼。 笑死塑顺,一個(gè)胖子當(dāng)著我的面吹牛汤求,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播严拒,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼扬绪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了裤唠?” 一聲冷哼從身側(cè)響起挤牛,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎巧骚,沒想到半個(gè)月后赊颠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體格二,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年竣蹦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了顶猜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡痘括,死狀恐怖长窄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情纲菌,我是刑警寧澤挠日,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站翰舌,受9級(jí)特大地震影響嚣潜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜椅贱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一懂算、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧庇麦,春花似錦计技、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至航棱,卻和暖如春睡雇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背丧诺。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工入桂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奄薇,地道東北人驳阎。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像馁蒂,于是被迫代替她去往敵國(guó)和親呵晚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • 姓名:周雪寧 學(xué)號(hào):1702110196 轉(zhuǎn)載:https://mp.weixin.qq.com/s/Si2jtA...
    周雪寧閱讀 6,045評(píng)論 0 13
  • 文/驚蟄七七 聽過很多道理,卻依然過不好這一生驶俊。這話是韓寒說(shuō)的娶耍,韓寒的電影《后會(huì)無(wú)期》的海報(bào)里的話。 這話很值得回...
    驚蟄七七閱讀 716評(píng)論 3 16
  • 今天晚上很不開心饼酿,真的很不開心榕酒,為了那個(gè)人的承諾,哥哥辭掉工作故俐,跑去找他想鹰,我還以為他真的變好了,想為我們著想了药版,但...
    安靜的女紙閱讀 211評(píng)論 0 0
  • 斷舍離教會(huì)我們?cè)谏钪袑W(xué)會(huì)放棄多余的人事物辑舷,使生活更樸素更純凈更純粹。然而當(dāng)我偶爾看到那些日本極端斷舍離的家庭照片...
    唯悟知足閱讀 728評(píng)論 0 2
  • 己所欲者槽片,巧施于人:一則以律惩妇、一則以利。人以利趨易明白筐乳,然律為何物歌殃?眾說(shuō)紛紜,理解不一蝙云∶ブ澹康德說(shuō)“在這個(gè)世界...
    三無(wú)農(nóng)夫閱讀 366評(píng)論 0 1