GPT系列:GPT-2模型結(jié)構(gòu)簡述和實踐

關(guān)鍵詞:GPTTransformer

內(nèi)容摘要

  • GPT的背景來源和發(fā)展簡述
  • GPT的自回歸工作方式
  • 圖解GPT-2的網(wǎng)絡(luò)結(jié)構(gòu)
  • GPT的解碼采樣策略
  • minGPT源碼分析和文本生成實戰(zhàn)

GPT的背景來源和發(fā)展簡述

GPT(Generative Pre-Trained Transformer,生成式預訓練Transformer模型),它是基于Transformer的Decoder解碼器在海量文本上訓練得到的預訓練模型。GPT采用自回歸的工作方式,能夠查看句子的一部分并且預測下一個單詞,不斷重復這個過程來生成連貫且適當上下文文本颓芭。
GPT的誕生早于Bert,相比于Bert針對的任務(wù)通常是文本分類柬赐、實體識別亡问、完型填空, GPT這種完全自行生成類似人類語言文本的任務(wù)更具挑戰(zhàn)性肛宋,是強人工智能的體現(xiàn)州藕,ChatGPT就是在GPT技術(shù)的基礎(chǔ)之上開發(fā)的交互式對話版本。


GPT的自回歸工作方式

給定一個前句酝陈,將它作為問題或者引導詞輸入給GPT床玻,GPT基于這個句子預測一個單詞,同時預測出的詞會加入到原始句子的結(jié)尾合并成一個新的句子作為形成下一次的輸入沉帮,這種工作方式被成為自回歸性锈死,如下圖所示

GPT的最回歸工作方式

在訓練階段GPT采用對文本進行shifted-right右移一位的方式構(gòu)造出輸入和輸出,在預測推理階段采用自回歸的方式通過持續(xù)地更新上下文窗口中的輸入來源源不斷的預測下去穆壕。


圖解GPT-2的網(wǎng)絡(luò)結(jié)構(gòu)

GPT系列自O(shè)penAI在2018年發(fā)布的第一代GPT-1開始待牵,以及后的GPT-2,GPT-3喇勋,他們的區(qū)別主要在于預訓練數(shù)據(jù)和模型參數(shù)的規(guī)模在不斷擴大缨该,以及訓練策略優(yōu)化等等,而模型網(wǎng)絡(luò)結(jié)構(gòu)并沒有大的改變川背,都是以堆疊Transformer的解碼器Decoder作為基座贰拿。

模型 發(fā)布時間 層數(shù) 頭數(shù) 詞向量長度 參數(shù)量 預訓練數(shù)據(jù)量
GPT-1 2018年6月 12 12 768 1.17億 5GB
GPT-2 2019年2月 48 25 1600 15億 40GB
GPT-3 2020年5月 96 96 12888 1750億 45TB

本篇采用GPT-2作為GPT系列網(wǎng)路結(jié)構(gòu)的說明。

GPT-1,GPT-2網(wǎng)絡(luò)結(jié)構(gòu)

上圖所示左側(cè)為GPT-1結(jié)構(gòu)渗常,右側(cè)為GPT-2結(jié)構(gòu)壮不,堆疊了12層Transformer的Decoder解碼器汗盘,GPT-2略微修改了Layer Norm層歸一化的位置皱碘,輸入層對上下文窗口長度大小(block size)的文本做建模隐孽,使用token embedding+位置編碼癌椿,輸出層獲取解碼向量信息健蕊,最后一個block對應的向量為下一個詞的信息表征,可以對該向量做分類任務(wù)完成序列的生成踢俄。
GPT-2本質(zhì)上沿用了Transformer的解碼器Self Attention機制缩功,在堆疊的每一層悼粮,當前時刻信息的表征嗤堰,只能使用當前詞和當前詞之前的信息,不能像Bert那樣既能看到左側(cè)的詞又能看到右側(cè)的詞檬寂,因為GPT是文本生成任務(wù)琳钉,而Bert是給定了全文的基礎(chǔ)上進行語意理解势木,兩者區(qū)別如下圖所示

GPT和Bert的區(qū)別

GPT的解碼采樣策略

GPT的堆疊Decoder結(jié)構(gòu)輸出的最后一個block的embedding,再結(jié)合Linear線性層和Softmax層映射到全部詞表歌懒,即可獲得下一個單詞的概率分布啦桌,GPT的解碼過程就是基于概率分布來確定下一個單詞是誰,顯然得分高的更應該被作為預測結(jié)果及皂。GPT常用的解碼策略有貪婪采樣和多項式采樣

  • 貪婪采樣:在每一步選擇概率最高的單詞作為結(jié)果甫男。這種方法簡單高效,但是可能會導致生成的文本過于單調(diào)和重復
  • 多項式采樣:在每一步根據(jù)概率分布作為權(quán)重验烧,隨機選擇單詞板驳。這種方法可以增加生成的多樣性,但是可能會導致生成的文本不連貫和無意義噪窘,出現(xiàn)比較離譜的生成結(jié)果

用PyTorch模擬一個基于多項式采樣的例子如下

>>> import torch
>>> weights = torch.Tensor([0, 1, 2, 3, 4, 5]) 
>>> cnt = {}
>>> for i in range(10000):
>>>     res = torch.multinomial(weights, 1).item()
>>>     cnt[res] = cnt.get(aaa, 0) + 1
>>> cnt
{5: 3302, 2: 1307, 3: 1994, 1: 707, 4: 2690}

采用torch.multinomial模擬10000次采樣笋庄,記錄下最終采樣的結(jié)果,結(jié)論是被采樣到的概率基本等于該單詞的權(quán)重占全部權(quán)重的比例倔监。
通常GPT采用多項式采樣結(jié)合Temperature溫度系數(shù)直砂,Top-K,Top-P來一起完成聯(lián)合采樣浩习。

Temperature溫度系數(shù)

溫度系數(shù)是一個大于0的值静暂,溫度系數(shù)會對概率得分分布做統(tǒng)一的縮放,具體做法是在softmax之前對原始得分除以溫度系數(shù)谱秽,公式如下

溫度系數(shù)公式

當溫度系數(shù)大于1時洽蛀,高得分單詞和低得分單詞的差距被縮小,導致多項式采樣的傾向更加均勻化疟赊,結(jié)果是生成的內(nèi)容多樣性更高郊供。當溫度系數(shù)小于1時,高得分單詞和低得分單詞的差距被放大近哟,導致多項式采樣的更加傾向于選擇得分高的單詞驮审,生成的內(nèi)容確定性更高。默認溫度系數(shù)等于1,此時等同于使用原始的概率分布不做任何縮放疯淫。地来。
使用PyTorch模擬溫度系數(shù)分別為2和0.5,結(jié)果如下

>>> torch.softmax(torch.tensor([1.0, 2.0, 3.0, 4.0]), -1)
tensor([0.0321, 0.0871, 0.2369, 0.6439])

>>> # 溫度系數(shù)為2
>>> torch.softmax(torch.tensor([1.0, 2.0, 3.0, 4.0]) / 2, -1)
tensor([0.1015, 0.1674, 0.2760, 0.4551])

>>> # 溫度系數(shù)為0.5
>>> torch.softmax(torch.tensor([1.0, 2.0, 3.0, 4.0]) / 0.5, -1)
tensor([0.0021, 0.0158, 0.1171, 0.8650])

顯然溫度系數(shù)越高為2時抽樣概率越平均熙掺,溫度系數(shù)越低為0.5時抽樣概率越極端未斑,對高分的頂部詞更加強化。

Top-K

Top-K指的是將詞表中所有單詞根據(jù)概率分布從高到低排序币绩,選取前K個作為采樣候選蜡秽,剩下的其他單詞永遠不會被采樣到,這樣做是可以將過于離譜的單詞直接排除在外缆镣,一定程度上提高了多項式采樣策略的準確性载城。同樣的k越大,生成的多樣性越高费就,k 越小诉瓦,生成的質(zhì)量一般越高。

Top-P

Top-P和Top-K類似力细,選取頭部單詞概率累加到閾值P截止睬澡,剩余的長尾單詞全部丟棄,例如P=95%眠蚂,單詞A的概率為0.85煞聪,單詞B的概率為0.11,頭部的兩個詞概率相加已經(jīng)達到95%逝慧,因此該步的采樣候選只有單詞A和單詞B昔脯,其他詞不會被采樣到。對位概率分布長尾的情況笛臣,Top-P相比于Top-K更加可以避免抽樣到一些概率過低的不相關(guān)的詞云稚。


minGPT源碼分析和文本生成實戰(zhàn)

minGPT項目是基于PyTorch實現(xiàn)的GPT-2,它包含GPT-2的訓練和推理沈堡,本篇以minGPT的chargpt例子作源碼分析静陈。chargpt是訓練用戶自定義的語料,訓練完成后基于用戶給到的文本可以實現(xiàn)自動續(xù)先诞丽,本例采用電視劇《狂飆》的部分章節(jié)作為語料灌給GPT-2進行訓練鲸拥。

模型參數(shù)概覽

定位到chargpt.py,首先作者定義了數(shù)據(jù)和模型相關(guān)的參數(shù)配置僧免,部分關(guān)鍵配置信息如下

data:
    block_size: 128
model:
    model_type: gpt-mini
    n_layer: 6
    n_head: 6
    n_embd: 192
    vocab_size: 2121
    block_size: 128
    embd_pdrop: 0.1
    resid_pdrop: 0.1
    attn_pdrop: 0.1
trainer:
    num_workers: 4
    max_iters: None
    batch_size: 64
    learning_rate: 0.0005
    betas: (0.9, 0.95)
    weight_decay: 0.1
    grad_norm_clip: 1.0
  • block_size:上下文窗口長度刑赶,也是輸入的最大文本長度為128
  • model_type:gpt模型類型,這里采用gpt-mini懂衩,一個小規(guī)模參數(shù)的gpt撞叨,它包含6層Decoder呛伴,6個自注意力頭,詞向量的embedding維度為192
  • num_workers:DataLoader加載數(shù)據(jù)的子進程數(shù)量谒所,可以加快訓練速度,對模型結(jié)果沒有影響
訓練數(shù)據(jù)構(gòu)造

需要用戶自己指定一個input.txt沛申,其內(nèi)容為合并了所有文本而成的一行字符串

text = open('input.txt', 'r').read()
train_dataset = CharDataset(config.data, text)

CharDataset基于PyTorch的Dataset構(gòu)造劣领,len和getitem實現(xiàn)如下

    def __len__(self):
        return len(self.data) - self.config.block_size

    def __getitem__(self, idx):
        # grab a chunk of (block_size + 1) characters from the data
        chunk = self.data[idx:idx + self.config.block_size + 1]
        # encode every character to an integer
        dix = [self.stoi[s] for s in chunk]
        # return as tensors
        x = torch.tensor(dix[:-1], dtype=torch.long)
        y = torch.tensor(dix[1:], dtype=torch.long)
        return x, y

采用上下文窗口在原文本上從前滑動到最后,生成文本長度減去上下文窗口大小的實例作為len铁材,getitem部分實現(xiàn)訓練的輸入x和目標y尖淘,每次選取129個連續(xù)的字符作為一個塊,然后以前128個作為x著觉,后128個作為y村生,實現(xiàn)shifted right。

gpt模型構(gòu)建

作者將配置參數(shù)傳遞給GPT類實例化一個GPT網(wǎng)絡(luò)

model = GPT(config.model)

在GPT類中實現(xiàn)了網(wǎng)路的各個元素饼丘,包括token embedding趁桃,位置編碼,Decoder肄鸽,Decoder結(jié)束后的層歸一化卫病,以及最后的概率分布線性映射層

self.transformer = nn.ModuleDict(dict(
            # TODO 輸入的embedding [10, 48]
            wte=nn.Embedding(config.vocab_size, config.n_embd),
            # TODO 位置編碼,block_size是上下文窗口 [6, 48]
            wpe=nn.Embedding(config.block_size, config.n_embd),
            drop=nn.Dropout(config.embd_pdrop),  # 0.1
            # TODO 隱藏層典徘,6層transformer
            h=nn.ModuleList([Block(config) for _ in range(config.n_layer)]),
            ln_f=nn.LayerNorm(config.n_embd),
        ))
# TODO 線性層輸出概率分布
self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False)

其中定義h為一個包含6層的堆疊Decoder蟀苛,Block實現(xiàn)了其中每一個Decoder單元,內(nèi)部包括點積注意力層逮诲,層歸一化帜平,前饋傳播層和殘差連接,實現(xiàn)如下

class Block(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.ln_1 = nn.LayerNorm(config.n_embd)
        # TODO 自回歸模型中的自注意力層
        self.attn = CausalSelfAttention(config)
        self.ln_2 = nn.LayerNorm(config.n_embd)
        self.mlp = nn.ModuleDict(dict(
            c_fc=nn.Linear(config.n_embd, 4 * config.n_embd),
            c_proj=nn.Linear(4 * config.n_embd, config.n_embd),
            act=NewGELU(),
            dropout=nn.Dropout(config.resid_pdrop),
        ))
        m = self.mlp
        # TODO feed forward層
        self.mlpf = lambda x: m.dropout(m.c_proj(m.act(m.c_fc(x))))  # MLP forward

    def forward(self, x):
        x = x + self.attn(self.ln_1(x))
        x = x + self.mlpf(self.ln_2(x))
        return x

forward中順序有所不同梅鹦,GPT-2中將層歸一化提前到了注意力操作之前裆甩。
核心的點積注意力在CausalSelfAttention中實現(xiàn),該類自己維護了下三角矩陣用于對后面的詞進行mask齐唆。

class CausalSelfAttention(nn.Module):
    def __init__(self, config):
        ...
        self.register_buffer("bias", torch.tril(torch.ones(config.block_size, config.block_size))
                             .view(1, 1, config.block_size, config.block_size))
    def forward(self, x):
        B, T, C = x.size()
        ...
        att = att.masked_fill(self.bias[:, :, :T, :T] == 0, float('-inf'))

作者先定義了一個基于block_size全局最大128×128的下三角矩陣bias淑掌,而實際在使用時,根據(jù)輸入文本的實際長度T從左上角開始取對應T×T形狀蝶念,無論T多長bias始終是下三角矩陣抛腕,接著將注意力矩陣上三角部分全部置為-inf,從而使得所有后詞信息被隔離媒殉。這個下三角掩碼不論輸入句子長度多長担敌,并且在每一層堆疊的Decoder上都會生效。
在網(wǎng)絡(luò)搭建好后廷蓉,作者構(gòu)建輸入層全封,它是由token embedding和位置編碼相加而成

b, t = idx.size()
pos = torch.arange(0, t, dtype=torch.long, device=device).unsqueeze(0)
tok_emb = self.transformer.wte(idx)  # token embeddings
pos_emb = self.transformer.wpe(pos)  # position embeddings
x = self.transformer.drop(tok_emb + pos_emb)

緊接著輸入到由6層Decode組成的隱層h马昙,GPT-2在Decoder結(jié)束之后又加了層歸一化ln_f,最終到線性映射層lm_head輸出預測到詞表的得分分布

for block in self.transformer.h:
    x = block(x)
x = self.transformer.ln_f(x)
logits = self.lm_head(x)
損失函數(shù)

損失采用每個預測位置的多分類交叉熵刹悴,一個批次下將所有樣本合并拉直行楞,最終所有位置的交叉熵取平均值作為最終損失

loss = None
if targets is not None:
    # TODO logits => [batch_size * seq_len, 10], target => [batch_size * seq_len, ]
    # TODO 該批次下 每條樣本土匀,每個位置下預測的多分類交叉熵損失 的平均值
    loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1), ignore_index=-1)
模型推理

在模型推理階段使用generate類方法子房,該方法定義了最大推理步長,溫度系數(shù)等參數(shù)就轧。作者給了個例子证杭,輸入一句話,對其進行自回歸預測下一個單詞妒御,其中最大預測100步解愤,溫度系數(shù)為1.0,采用多項式采樣乎莉,限制Top-K為10

with torch.no_grad():
    context = "高啟強被捕之后"
    # TODO [seq_len, ] => [batch_size, seq_len]
    x = torch.tensor([train_dataset.stoi[s] for s in context], dtype=torch.long)[None, ...].to(trainer.device)
    y = model.generate(x, 100, temperature=1.0, do_sample=True, top_k=10)[0]
    completion = ''.join([train_dataset.itos[int(i)] for i in y])

跟進generate方法查看源碼實現(xiàn)

    def generate(self, idx, max_new_tokens, temperature=1.0, do_sample=False, top_k=None):
        # TODO idx => [batch_size, 4]
        for _ in range(max_new_tokens):
            # TODO idx_cond不定長送讲,只要低于最大上下文窗口即可, 如果文本超過block上下文窗口(輸入序列最大長度),向前截取惋啃,取最后和block等長的
            idx_cond = idx if idx.size(1) <= self.block_size else idx[:, -self.block_size:]
            # TODO [batch_size, seq_len, 10]
            logits, _ = self(idx_cond)
            # TODO [batch_size, 10] 拿到最后一個block位置的作為下一個單詞的概率得分分布
            # TODO temperature 溫度系數(shù), 默認為1,不對分數(shù)做任何縮放
            # TODO 溫度系數(shù)越大李茫,得分差距縮短,多樣性更大肥橙,溫度系數(shù)越小魄宏,得分差距加大,多樣性更低
            logits = logits[:, -1, :] / temperature
            # TODO 只選取得分最大的topk個
            if top_k is not None:
                # TODO 輸出最大值和最大值的索引
                # TODO v => [batch_size, topk]
                v, _ = torch.topk(logits, top_k)
                # TODO v[:, [-1]] => [batch_size, 1], topk的最小值, 將沒有進入topk的全部改為-inf
                logits[logits < v[:, [-1]]] = -float('Inf')
            # TODO [batch_size, 10] 經(jīng)過溫度系數(shù)和topk壓縮之后使用softmax轉(zhuǎn)化為概率
            probs = F.softmax(logits, dim=-1)
            if do_sample:
                # TODO 根據(jù)概率作為權(quán)重采樣其中一個存筏,概率越大被采樣到的概率越大
                # TODO 溫度系數(shù)的大小會直接影響這個地方的采樣權(quán)重,溫度系數(shù)決定了在topk范圍內(nèi)的采樣多樣性
                # TODO topK會把沒有進入topk的softmax概率轉(zhuǎn)化為0,使得永遠不能被采樣到, topk決定了哪些會被采樣到
                # TODO idx_next => [batch_size, 1]
                idx_next = torch.multinomial(probs, num_samples=1)
            else:
                # TODO 貪婪搜索 [batch_size, 1]
                _, idx_next = torch.topk(probs, k=1, dim=-1)
            # TODO 將預測的新單詞拼接到原始輸入上作為新的輸入
            idx = torch.cat((idx, idx_next), dim=1)

        return idx

其中idx為自回歸維護的輸入宠互,每次預測完下一個單詞拼接到結(jié)尾覆蓋更新idx

idx = torch.cat((idx, idx_next), dim=1)

在每次推理過程中只使用上下文窗口中最后一個block的輸出作為下個單詞的推理依據(jù)

logits = logits[:, -1, :] / temperature

在采樣策略順序上,先進行溫度系數(shù)縮放椭坚,再進行top_k篩選予跌,最后通過softmax壓縮為概率,最終采用torch.multinomial進行多項式采樣善茎,top_k的實現(xiàn)是將所有非top_k的位置得分置為-inf券册,使得softmax壓縮為0,只要存在其他非0權(quán)重垂涯,多項式采樣永遠不會將0采樣出來烁焙,通過這種方法將非top_k在解碼中剔除。

模型訓練

基于《狂飆》語料灌入定義好的gpt-mini耕赘,每迭代500詞打印一次模型推理的結(jié)果骄蝇,我們給到例句“高啟強被捕之后”,查看隨著損失的收斂操骡,模型預測接下來50個詞的結(jié)果

迭代次數(shù) 損失 生成結(jié)果
0 7.696 高啟強被捕之后操”徐”九火,姓”赚窃,越,尬””越上”越徐噎曲噎噎的貓上建驗建岔激,姓”越拼越津噎勒极,”建,虑鼎,的辱匿,,震叙,上”上”越
1000 0.671 高啟強被捕之后,高啟強看著安靜地洗得很好的攤子散休。音在順道我們送禮物就去躲到了解決媒楼。門診大夫都準備,你再幫忙戚丸!”“不
2000 0.288 高啟強被捕之后的遺照片的下醫(yī)院常車牌划址,刑警隊員禮貌似乎跑斷。耳機里的現(xiàn)場館情不倫次限府,雖然快地想立即決定:將自己在將
3000 0.200 高啟強被捕之后夺颤,安欣也十分爽快地答應幫忙——只要高家兄弟拿出三萬元。正在高啟強一籌莫展的時候胁勺,唐小龍介紹了一個門道
7500 0.107 高啟強被捕之后世澜,高啟強終于弄明白了是怎么回事。原來唐家兄弟自從上次見到安欣和李響之后署穗,便四處打聽安欣的身份寥裂,確定了
8500 0.095 高啟強被捕之后隨即打開懷里的包,點出一本厚的法醫(yī)學專著擺在桌上案疲,得意地指了指封恰,“看看『址龋”安欣一怔:“你怎么看這個诺舔?

隨著損失的收斂,生成結(jié)果逐漸變得連貫备畦,尤其最后一句從語法和語義上基本挑不出什么問題低飒,也預測出了訓練集中的部分原文。此處只是一個例子在給定文本上從頭訓練一個簡單的小規(guī)模gpt網(wǎng)絡(luò)懂盐,如果期望更好的效果需要基于更大規(guī)模數(shù)據(jù)的預訓練gpt模型逸嘀。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市允粤,隨后出現(xiàn)的幾起案子崭倘,更是在濱河造成了極大的恐慌翼岁,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件司光,死亡現(xiàn)場離奇詭異琅坡,居然都是意外死亡,警方通過查閱死者的電腦和手機残家,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進店門榆俺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坞淮,你說我怎么就攤上這事茴晋。” “怎么了回窘?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵诺擅,是天一觀的道長。 經(jīng)常有香客問我啡直,道長烁涌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任酒觅,我火速辦了婚禮撮执,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舷丹。我一直安慰自己抒钱,他們只是感情好,可當我...
    茶點故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布颜凯。 她就那樣靜靜地躺著继效,像睡著了一般。 火紅的嫁衣襯著肌膚如雪装获。 梳的紋絲不亂的頭發(fā)上瑞信,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天,我揣著相機與錄音穴豫,去河邊找鬼凡简。 笑死,一個胖子當著我的面吹牛精肃,可吹牛的內(nèi)容都是我干的秤涩。 我是一名探鬼主播,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼司抱,長吁一口氣:“原來是場噩夢啊……” “哼筐眷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起习柠,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤匀谣,失蹤者是張志新(化名)和其女友劉穎照棋,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體武翎,經(jīng)...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡烈炭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了宝恶。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片符隙。...
    茶點故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖垫毙,靈堂內(nèi)的尸體忽然破棺而出霹疫,到底是詐尸還是另有隱情,我是刑警寧澤综芥,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布丽蝎,位于F島的核電站,受9級特大地震影響毫痕,放射性物質(zhì)發(fā)生泄漏征峦。R本人自食惡果不足惜迟几,卻給世界環(huán)境...
    茶點故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一消请、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧类腮,春花似錦臊泰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至厂抽,卻和暖如春需频,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背筷凤。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工昭殉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人藐守。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓挪丢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親卢厂。 傳聞我的和親對象是個殘疾皇子乾蓬,可洞房花燭夜當晚...
    茶點故事閱讀 42,700評論 2 345

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