動(dòng)手學(xué)深度學(xué)習(xí)PyTorch版-Task01/Task02

1. 線性回歸

(僅供學(xué)習(xí)班打卡使用)

損失函數(shù)

在模型訓(xùn)練中探入,我們需要衡量價(jià)格預(yù)測值與真實(shí)值之間的誤差箫锤。通常我們會(huì)選取一個(gè)非負(fù)數(shù)作為誤差卑吭,且數(shù)值越小表示誤差越小锦亦。一個(gè)常用的選擇是平方函數(shù)。 它在評估索引為的樣本誤差的表達(dá)式為

l^{(i)}(\mathbf{w}, b) = \frac{1}{2}\left(\hat{y}^{(i)} - y^{(i)}\right)^2,

L(\mathbf{w}, b) =\frac{1}{n}\sum_{i=1}^n l^{(i)}(\mathbf{w}, b) =\frac{1}{n} \sum_{i=1}^n \frac{1}{2}\left(\mathbf{w}^\top \mathbf{x}^{(i)} + b - y^{(i)}\right)^2.

優(yōu)化函數(shù) - 隨機(jī)梯度下降

(\mathbf{w},b) \leftarrow (\mathbf{w},b) - \frac{\eta} {|\mathcal{B}|} \sum_{i \in \mathcal{B}} \partial_{(\mathbf{w},b)} l^{(i)}(\mathbf{w},b)

定義優(yōu)化函數(shù)

在這里優(yōu)化函數(shù)使用的是小批量隨機(jī)梯度下降:

 def sgd(params, lr, batch_size): 
    for param in params:
        param.data -= lr * param.grad / batch_size # ues .data to operate param without gradient track

訓(xùn)練模型(從零實(shí)現(xiàn))

# super parameters init
lr = 0.03
num_epochs = 5
#兩個(gè)超參數(shù)

net = linreg
loss = squared_loss

# training
for epoch in range(num_epochs):  # training repeats num_epochs times
    # in each epoch, all the samples in dataset will be used once
    ## 訓(xùn)練模型一共需要num_epochs個(gè)迭代周期
    # 在每一個(gè)迭代周期中嫂拴,會(huì)使用訓(xùn)練數(shù)據(jù)集中所有樣本一次(假設(shè)樣本數(shù)能夠被批量大小整除)播揪。X
    # 和y分別是小批量樣本的特征和標(biāo)簽
    # X is the feature and y is the label of a batch sample
    for X, y in data_iter(batch_size, features, labels):
        l = loss(net(X, w, b), y).sum()  
        # calculate the gradient of batch sample loss 
        l.backward()  
        # using small batch random gradient descent to iter model parameters
        sgd([w, b], lr, batch_size)  
        # reset parameter gradient參數(shù)梯度數(shù)據(jù)清零,以備下一次存儲(chǔ)新的梯度數(shù)據(jù)筒狠。
        w.grad.data.zero_()
        b.grad.data.zero_()
    train_l = loss(net(features, w, b), labels)
    print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item()))

線性回歸模型使用pytorch的簡潔實(shí)現(xiàn)

import torch
from torch import nn
import numpy as np
torch.manual_seed(1)

print(torch.__version__)
torch.set_default_tensor_type('torch.FloatTensor')

生成數(shù)據(jù)集

num_inputs = 2
num_examples = 1000

true_w = [2, -3.4]
true_b = 4.2

features = torch.tensor(np.random.normal(0, 1, (num_examples, num_inputs)), dtype=torch.float)
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)

定義模型

class LinearNet(nn.Module):
    def __init__(self, n_feature):
        super(LinearNet, self).__init__()      # call father function to init 
        self.linear = nn.Linear(n_feature, 1)  # function prototype: `torch.nn.Linear(in_features, out_features, bias=True)`

    def forward(self, x):
        y = self.linear(x)
        return y
    
net = LinearNet(num_inputs)
print(net)
# ways to init a multilayer network
# method one
net = nn.Sequential(
    nn.Linear(num_inputs, 1)
    # other layers can be added here
    )

# method two
net = nn.Sequential()
net.add_module('linear', nn.Linear(num_inputs, 1))
# net.add_module ......

# method three
from collections import OrderedDict
net = nn.Sequential(OrderedDict([
          ('linear', nn.Linear(num_inputs, 1))
          # ......
        ]))

print(net)
print(net[0])

定義損失函數(shù)

loss = nn.MSELoss()    # nn built-in squared loss function
                       # function prototype: `torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')`

定義優(yōu)化函數(shù)

import torch.optim as optim

optimizer = optim.SGD(net.parameters(), lr=0.03)   # built-in random gradient descent function
print(optimizer)  # function prototype: `torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False)`

2.softmax回歸

softmax的基本概念

  • 分類問題
    一個(gè)簡單的圖像分類問題猪狈,輸入圖像的高和寬均為2像素,色彩為灰度辩恼。
    圖像中的4像素分別記為x_1, x_2, x_3, x_4雇庙。
    假設(shè)真實(shí)標(biāo)簽為狗、貓或者雞灶伊,這些標(biāo)簽對應(yīng)的離散值為y_1, y_2, y_3疆前。
    我們通常使用離散的數(shù)值來表示類別,例如y_1=1, y_2=2, y_3=3谁帕。

  • 權(quán)重矢量
    \begin{aligned} o_1 &= x_1 w_{11} + x_2 w_{21} + x_3 w_{31} + x_4 w_{41} +b_1 \end{aligned}

\begin{aligned} o_2 &= x_1 w_{12} + x_2 w_{22} + x_3 w_{32} + x_4 w_{42} + b_2 \end{aligned}

\begin{aligned} o_2 &= x_1 w_{13} + x_2 w_{23} + x_3 w_{33} + x_4 w_{43} + b_3 \end{aligned}

  • 神經(jīng)網(wǎng)絡(luò)圖
    下圖用神經(jīng)網(wǎng)絡(luò)圖描繪了上面的計(jì)算峡继。softmax回歸同線性回歸一樣,也是一個(gè)單層神經(jīng)網(wǎng)絡(luò)匈挖。由于每個(gè)輸出o_1, o_2, o_3的計(jì)算都要依賴于所有的輸入x_1, x_2, x_3, x_4碾牌,softmax回歸的輸出層也是一個(gè)全連接層。

\begin{aligned}softmax回歸是一個(gè)單層神經(jīng)網(wǎng)絡(luò)\end{aligned}

既然分類問題需要得到離散的預(yù)測輸出儡循,一個(gè)簡單的辦法是將輸出值o_i當(dāng)作預(yù)測類別是i的置信度舶吗,并將值最大的輸出所對應(yīng)的類作為預(yù)測輸出,即輸出 \underset{i}{\arg\max} o_i择膝。例如誓琼,如果o_1,o_2,o_3分別為0.1,10,0.1,由于o_2最大,那么預(yù)測類別為2腹侣,其代表貓叔收。

  • 輸出問題
    直接使用輸出層的輸出有兩個(gè)問題:
    1. 一方面,由于輸出層的輸出值的范圍不確定傲隶,我們難以直觀上判斷這些值的意義饺律。例如,剛才舉的例子中的輸出值10表示“很置信”圖像類別為貓跺株,因?yàn)樵撦敵鲋凳瞧渌麅深惖妮敵鲋档?00倍复濒。但如果o_1=o_3=10^3,那么輸出值10卻又表示圖像類別為貓的概率很低乒省。
    2. 另一方面巧颈,由于真實(shí)標(biāo)簽是離散值,這些離散值與不確定范圍的輸出值之間的誤差難以衡量袖扛。

softmax運(yùn)算符(softmax operator)解決了以上兩個(gè)問題砸泛。它通過下式將輸出值變換成值為正且和為1的概率分布:

\hat{y}_1, \hat{y}_2, \hat{y}_3 = \text{softmax}(o_1, o_2, o_3)

其中

\hat{y}1 = \frac{ \exp(o_1)}{\sum_{i=1}^3 \exp(o_i)},\quad \hat{y}2 = \frac{ \exp(o_2)}{\sum_{i=1}^3 \exp(o_i)},\quad \hat{y}3 = \frac{ \exp(o_3)}{\sum_{i=1}^3 \exp(o_i)}.

容易看出\hat{y}_1 + \hat{y}_2 + \hat{y}_3 = 10 \leq \hat{y}_1, \hat{y}_2, \hat{y}_3 \leq 1,因此\hat{y}_1, \hat{y}_2, \hat{y}_3是一個(gè)合法的概率分布攻锰。這時(shí)候晾嘶,如果\hat{y}_2=0.8,不管\hat{y}_1\hat{y}_3的值是多少娶吞,我們都知道圖像類別為貓的概率是80%。此外械姻,我們注意到

\underset{i}{\arg\max} o_i = \underset{i}{\arg\max} \hat{y}_i

因此softmax運(yùn)算不改變預(yù)測類別輸出妒蛇。

  • 計(jì)算效率
    • 單樣本矢量計(jì)算表達(dá)式
      為了提高計(jì)算效率,我們可以將單樣本分類通過矢量計(jì)算來表達(dá)楷拳。在上面的圖像分類問題中绣夺,假設(shè)softmax回歸的權(quán)重和偏差參數(shù)分別為

\boldsymbol{W} = \begin{bmatrix} w_{11} & w_{12} & w_{13} \\ w_{21} & w_{22} & w_{23} \\ w_{31} & w_{32} & w_{33} \\ w_{41} & w_{42} & w_{43} \end{bmatrix} \quad \boldsymbol\quad \boldsymbol = \begin{bmatrix} b_1 & b_2 & b_3 \end{bmatrix}

設(shè)高和寬分別為2個(gè)像素的圖像樣本i的特征為

\boldsymbol{x}^{(i)} = \begin{bmatrix}x_1^{(i)} & x_2^{(i)} & x_3^{(i)} & x_4^{(i)}\end{bmatrix}

輸出層的輸出為

\boldsymbol{o}^{(i)} = \begin{bmatrix}o_1^{(i)} & o_2^{(i)} & o_3^{(i)}\end{bmatrix},

預(yù)測為狗欢揖、貓或雞的概率分布為

\boldsymbol{\hat{y}}^{(i)} = \begin{bmatrix}\hat{y}_1^{(i)} & \hat{y}_2^{(i)} & \hat{y}_3^{(i)}\end{bmatrix}

softmax回歸對樣本i分類的矢量計(jì)算表達(dá)式為

\begin{aligned} \boldsymbol{o}^{(i)} &= \boldsymbol{x}^{(i)} \boldsymbol{W} + \boldsymbol陶耍,\\ \boldsymbol{\hat{y}}^{(i)} &= \text{softmax}(\boldsymbol{o}^{(i)}) \end{aligned}

  • 小批量矢量計(jì)算表達(dá)式
    為了進(jìn)一步提升計(jì)算效率,我們通常對小批量數(shù)據(jù)做矢量計(jì)算她混。廣義上講烈钞,給定一個(gè)小批量樣本,其批量大小為n坤按,輸入個(gè)數(shù)(特征數(shù))為d毯欣,輸出個(gè)數(shù)(類別數(shù))為q。設(shè)批量特征為\boldsymbol{X} \in \mathbb{R}^{n \times d}臭脓。假設(shè)softmax回歸的權(quán)重和偏差參數(shù)分別為\boldsymbol{W} \in \mathbb{R}^{d \times q}\boldsymbol酗钞 \in \mathbb{R}^{1 \times q}。softmax回歸的矢量計(jì)算表達(dá)式為

\begin{aligned} \boldsymbol{O} &= \boldsymbol{X} \boldsymbol{W} + \boldsymbol,\\ \boldsymbol{\hat{Y}} &= \text{softmax}(\boldsymbol{O}), \end{aligned}

其中的加法運(yùn)算使用了廣播機(jī)制砚作,\boldsymbol{O}, \boldsymbol{\hat{Y}} \in \mathbb{R}^{n \times q}且這兩個(gè)矩陣的第i行分別為樣本i的輸出\boldsymbol{o}^{(i)}和概率分布\boldsymbol{\hat{y}}^{(i)}窘奏。

交叉熵?fù)p失函數(shù)

對于樣本i,我們構(gòu)造向量\boldsymbol{y}^{(i)}\in \mathbb{R}^{q} 葫录,使其第y^{(i)}(樣本i類別的離散數(shù)值)個(gè)元素為1着裹,其余為0。這樣我們的訓(xùn)練目標(biāo)可以設(shè)為使預(yù)測概率分布\boldsymbol{\hat y}^{(i)}盡可能接近真實(shí)的標(biāo)簽概率分布\boldsymbol{y}^{(i)}压昼。

  • 平方損失估計(jì)

\begin{aligned}Loss = |\boldsymbol{\hat y}^{(i)}-\boldsymbol{y}^{(i)}|^2/2\end{aligned}

然而求冷,想要預(yù)測分類結(jié)果正確,我們其實(shí)并不需要預(yù)測概率完全等于標(biāo)簽概率窍霞。例如匠题,在圖像分類的例子里,如果y^{(i)}=3但金,那么我們只需要\hat{y}^{(i)}_3比其他兩個(gè)預(yù)測值\hat{y}^{(i)}_1\hat{y}^{(i)}_2大就行了韭山。即使\hat{y}^{(i)}_3值為0.6,不管其他兩個(gè)預(yù)測值為多少冷溃,類別預(yù)測均正確钱磅。而平方損失則過于嚴(yán)格,例如\hat y^{(i)}_1=\hat y^{(i)}_2=0.2\hat y^{(i)}_1=0, \hat y^{(i)}_2=0.4的損失要小很多似枕,雖然兩者都有同樣正確的分類預(yù)測結(jié)果盖淡。

改善上述問題的一個(gè)方法是使用更適合衡量兩個(gè)概率分布差異的測量函數(shù)。其中凿歼,交叉熵(cross entropy)是一個(gè)常用的衡量方法:

H\left(\boldsymbol y^{(i)}, \boldsymbol {\hat y}^{(i)}\right ) = -\sum_{j=1}^q y_j^{(i)} \log \hat y_j^{(i)},

其中帶下標(biāo)的y_j^{(i)}是向量\boldsymbol y^{(i)}中非0即1的元素褪迟,需要注意將它與樣本i類別的離散數(shù)值,即不帶下標(biāo)的y^{(i)}區(qū)分答憔。在上式中味赃,我們知道向量\boldsymbol y^{(i)}中只有第y^{(i)}個(gè)元素y^{(i)}{y^{(i)}}為1,其余全為0虐拓,于是H(\boldsymbol y^{(i)}, \boldsymbol {\hat y}^{(i)}) = -\log \hat y_{y^{(i)}}^{(i)}心俗。也就是說,交叉熵只關(guān)心對正確類別的預(yù)測概率蓉驹,因?yàn)橹灰渲底銐虼蟪情唬涂梢源_保分類結(jié)果正確。當(dāng)然戒幔,遇到一個(gè)樣本有多個(gè)標(biāo)簽時(shí)吠谢,例如圖像里含有不止一個(gè)物體時(shí),我們并不能做這一步簡化诗茎。但即便對于這種情況工坊,交叉熵同樣只關(guān)心對圖像中出現(xiàn)的物體類別的預(yù)測概率献汗。

假設(shè)訓(xùn)練數(shù)據(jù)集的樣本數(shù)為n,交叉熵?fù)p失函數(shù)定義為
\ell(\boldsymbol{\Theta}) = \frac{1}{n} \sum_{i=1}^n H\left(\boldsymbol y^{(i)}, \boldsymbol {\hat y}^{(i)}\right ),

其中\boldsymbol{\Theta}代表模型參數(shù)王污。同樣地罢吃,如果每個(gè)樣本只有一個(gè)標(biāo)簽,那么交叉熵?fù)p失可以簡寫成\ell(\boldsymbol{\Theta}) = -(1/n) \sum_{i=1}^n \log \hat y_{y^{(i)}}^{(i)}昭齐。從另一個(gè)角度來看尿招,我們知道最小化\ell(\boldsymbol{\Theta})等價(jià)于最大化\exp(-n\ell(\boldsymbol{\Theta}))=\prod_{i=1}^n \hat y_{y^{(i)}}^{(i)},即最小化交叉熵?fù)p失函數(shù)等價(jià)于最大化訓(xùn)練數(shù)據(jù)集所有標(biāo)簽類別的聯(lián)合預(yù)測概率阱驾。

softmax回歸模型

\begin{aligned} \boldsymbol{o}^{(i)} &= \boldsymbol{x}^{(i)} \boldsymbol{W} + \boldsymbol就谜,\\ \boldsymbol{\hat{y}}^{(i)} &= \text{softmax}(\boldsymbol{o}^{(i)})\end{aligned}

def net(X):
    return softmax(torch.mm(X.view((-1, num_inputs)), W) + b)

定義損失函數(shù)

H\left(\boldsymbol y^{(i)}, \boldsymbol {\hat y}^{(i)}\right ) = -\sum_{j=1}^q y_j^{(i)} \log \hat y_j^{(i)},

\ell(\boldsymbol{\Theta}) = \frac{1}{n} \sum_{i=1}^n H\left(\boldsymbol y^{(i)}, \boldsymbol {\hat y}^{(i)}\right ),

\ell(\boldsymbol{\Theta}) = -(1/n) \sum_{i=1}^n \log \hat y_{y^{(i)}}^{(i)}

y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
y = torch.LongTensor([0, 2])
y_hat.gather(1, y.view(-1, 1))
def cross_entropy(y_hat, y):
    return - torch.log(y_hat.gather(1, y.view(-1, 1)))

定義準(zhǔn)確率

def accuracy(y_hat, y):
    return (y_hat.argmax(dim=1) == y).float().mean().item()
print(accuracy(y_hat, y))
def evaluate_accuracy(data_iter, net):
    acc_sum, n = 0.0, 0
    for X, y in data_iter:
        acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
        n += y.shape[0]
    return acc_sum / n

訓(xùn)練模型

num_epochs, lr = 5, 0.1

# 本函數(shù)已保存在d2lzh_pytorch包中方便以后使用
def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size,
              params=None, lr=None, optimizer=None):
    for epoch in range(num_epochs):
        train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
        for X, y in train_iter:
            y_hat = net(X)
            l = loss(y_hat, y).sum()
            
            # 梯度清零
            if optimizer is not None:
                optimizer.zero_grad()
            elif params is not None and params[0].grad is not None:
                for param in params:
                    param.grad.data.zero_()
            
            l.backward()
            if optimizer is None:
                d2l.sgd(params, lr, batch_size)
            else:
                optimizer.step() 
            
            
            train_l_sum += l.item()
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
            n += y.shape[0]
        test_acc = evaluate_accuracy(test_iter, net)
        print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'
              % (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc))

train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, batch_size, [W, b], lr)

模型預(yù)測

現(xiàn)在我們的模型訓(xùn)練完了,可以進(jìn)行一下預(yù)測里覆,我們的這個(gè)模型訓(xùn)練的到底準(zhǔn)確不準(zhǔn)確丧荐。
現(xiàn)在就可以演示如何對圖像進(jìn)行分類了。給定一系列圖像(第三行圖像輸出)喧枷,我們比較一下它們的真實(shí)標(biāo)簽(第一行文本輸出)和模型預(yù)測結(jié)果(第二行文本輸出)虹统。

X, y = iter(test_iter).next()

true_labels = d2l.get_fashion_mnist_labels(y.numpy())
pred_labels = d2l.get_fashion_mnist_labels(net(X).argmax(dim=1).numpy())
titles = [true + '\n' + pred for true, pred in zip(true_labels, pred_labels)]

d2l.show_fashion_mnist(X[0:9], titles[0:9])

多層感知機(jī)的基本知識(shí)

深度學(xué)習(xí)主要關(guān)注多層模型。在這里隧甚,我們將以多層感知機(jī)(multilayer perceptron车荔,MLP)為例,介紹多層神經(jīng)網(wǎng)絡(luò)的概念戚扳。

隱藏層

下圖展示了一個(gè)多層感知機(jī)的神經(jīng)網(wǎng)絡(luò)圖忧便,它含有一個(gè)隱藏層,該層中有5個(gè)隱藏單元帽借。

image

表達(dá)公式

具體來說茬腿,給定一個(gè)小批量樣本\boldsymbol{X} \in \mathbb{R}^{n \times d},其批量大小為n宜雀,輸入個(gè)數(shù)為d。假設(shè)多層感知機(jī)只有一個(gè)隱藏層握础,其中隱藏單元個(gè)數(shù)為h辐董。記隱藏層的輸出(也稱為隱藏層變量或隱藏變量)為\boldsymbol{H},有\boldsymbol{H} \in \mathbb{R}^{n \times h}禀综。因?yàn)殡[藏層和輸出層均是全連接層简烘,可以設(shè)隱藏層的權(quán)重參數(shù)和偏差參數(shù)分別為\boldsymbol{W}_h \in \mathbb{R}^{d \times h}\boldsymbol缎浇_h \in \mathbb{R}^{1 \times h}笛臣,輸出層的權(quán)重和偏差參數(shù)分別為\boldsymbol{W}_o \in \mathbb{R}^{h \times q}\boldsymbol_o \in \mathbb{R}^{1 \times q}条摸。

我們先來看一種含單隱藏層的多層感知機(jī)的設(shè)計(jì)欠窒。其輸出\boldsymbol{O} \in \mathbb{R}^{n \times q}的計(jì)算為

\begin{aligned} \boldsymbol{H} &= \boldsymbol{X} \boldsymbol{W}_h + \boldsymbol覆旭_h,\\ \boldsymbol{O} &= \boldsymbol{H} \boldsymbol{W}_o + \boldsymbol_o, \end{aligned}

也就是將隱藏層的輸出直接作為輸出層的輸入。如果將以上兩個(gè)式子聯(lián)立起來型将,可以得到

\boldsymbol{O} = (\boldsymbol{X} \boldsymbol{W}_h + \boldsymbol寂祥_h)\boldsymbol{W}_o + \boldsymbol_o = \boldsymbol{X} \boldsymbol{W}_h\boldsymbol{W}_o + \boldsymbol七兜_h \boldsymbol{W}_o + \boldsymbol丸凭_o.

從聯(lián)立后的式子可以看出,雖然神經(jīng)網(wǎng)絡(luò)引入了隱藏層腕铸,卻依然等價(jià)于一個(gè)單層神經(jīng)網(wǎng)絡(luò):其中輸出層權(quán)重參數(shù)為\boldsymbol{W}_h\boldsymbol{W}_o惜犀,偏差參數(shù)為\boldsymbol_h \boldsymbol{W}_o + \boldsymbol狠裹_o虽界。不難發(fā)現(xiàn),即便再添加更多的隱藏層酪耳,以上設(shè)計(jì)依然只能與僅含輸出層的單層神經(jīng)網(wǎng)絡(luò)等價(jià)浓恳。

激活函數(shù)

上述問題的根源在于全連接層只是對數(shù)據(jù)做仿射變換(affine transformation),而多個(gè)仿射變換的疊加仍然是一個(gè)仿射變換碗暗。解決問題的一個(gè)方法是引入非線性變換颈将,例如對隱藏變量使用按元素運(yùn)算的非線性函數(shù)進(jìn)行變換,然后再作為下一個(gè)全連接層的輸入言疗。這個(gè)非線性函數(shù)被稱為激活函數(shù)(activation function)晴圾。

下面我們介紹幾個(gè)常用的激活函數(shù):

1. ReLU函數(shù)

ReLU(rectified linear unit)函數(shù)提供了一個(gè)很簡單的非線性變換。給定元素x噪奄,該函數(shù)定義為

\text{ReLU}(x) = \max(x, 0).

可以看出死姚,ReLU函數(shù)只保留正數(shù)元素,并將負(fù)數(shù)元素清零勤篮。為了直觀地觀察這一非線性變換都毒,我們先定義一個(gè)繪圖函數(shù)xyplot。

%matplotlib inline
import torch
import numpy as np
import matplotlib.pyplot as plt
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l
print(torch.__version__)
def xyplot(x_vals, y_vals, name):
    # d2l.set_figsize(figsize=(5, 2.5))
    plt.plot(x_vals.detach().numpy(), y_vals.detach().numpy())
    plt.xlabel('x')
    plt.ylabel(name + '(x)')
x = torch.arange(-8.0, 8.0, 0.1, requires_grad=True)
y = x.relu()
xyplot(x, y, 'relu')
y.sum().backward()
xyplot(x, x.grad, 'grad of relu')

2.Sigmoid函數(shù)

sigmoid函數(shù)可以將元素的值變換到0和1之間:

\text{sigmoid}(x) = \frac{1}{1+\exp(-x)}.

y = x.sigmoid()
xyplot(x, y, 'sigmoid')
x.grad.zero_()
y.sum().backward()
xyplot(x, x.grad, 'grad of sigmoid')

3.tanh函數(shù)

tanh(雙曲正切)函數(shù)可以將元素的值變換到-1和1之間:

\text{tanh}(x) = \frac{1 - \exp(-2x)}{1 + \exp(-2x)}.

我們接著繪制tanh函數(shù)碰缔。當(dāng)輸入接近0時(shí)账劲,tanh函數(shù)接近線性變換。雖然該函數(shù)的形狀和sigmoid函數(shù)的形狀很像金抡,但tanh函數(shù)在坐標(biāo)系的原點(diǎn)上對稱瀑焦。

y = x.tanh()
xyplot(x, y, 'tanh')
x.grad.zero_()
y.sum().backward()
xyplot(x, x.grad, 'grad of tanh')

關(guān)于激活函數(shù)的選擇

ReLu函數(shù)是一個(gè)通用的激活函數(shù),目前在大多數(shù)情況下使用梗肝。但是榛瓮,ReLU函數(shù)只能在隱藏層中使用。

用于分類器時(shí)巫击,sigmoid函數(shù)及其組合通常效果更好禀晓。由于梯度消失問題精续,有時(shí)要避免使用sigmoid和tanh函數(shù)。

在神經(jīng)網(wǎng)絡(luò)層數(shù)較多的時(shí)候匆绣,最好使用ReLu函數(shù)驻右,ReLu函數(shù)比較簡單計(jì)算量少,而sigmoid和tanh函數(shù)計(jì)算量大很多崎淳。

在選擇激活函數(shù)的時(shí)候可以先選用ReLu函數(shù)如果效果不理想可以嘗試其他激活函數(shù)堪夭。

循環(huán)神經(jīng)網(wǎng)絡(luò)

循環(huán)神經(jīng)網(wǎng)絡(luò)

語言模型

一段自然語言文本可以看作是一個(gè)離散時(shí)間序列,給定一個(gè)長度為T的詞的序列w_1, w_2, \ldots, w_T拣凹,語言模型的目標(biāo)就是評估該序列是否合理森爽,即計(jì)算該序列的概率:

P(w_1, w_2, \ldots, w_T)

本節(jié)我們介紹基于統(tǒng)計(jì)的語言模型,主要是n元語法(n-gram)嚣镜。在后續(xù)內(nèi)容中爬迟,我們將會(huì)介紹基于神經(jīng)網(wǎng)絡(luò)的語言模型。

語言模型

一段自然語言文本可以看作是一個(gè)離散時(shí)間序列菊匿,給定一個(gè)長度為T的詞的序列w_1, w_2, \ldots, w_T付呕,語言模型的目標(biāo)就是評估該序列是否合理,即計(jì)算該序列的概率:

P(w_1, w_2, \ldots, w_T).

本節(jié)我們介紹基于統(tǒng)計(jì)的語言模型跌捆,主要是n元語法(n-gram)徽职。在后續(xù)內(nèi)容中,我們將會(huì)介紹基于神經(jīng)網(wǎng)絡(luò)的語言模型佩厚。

語言模型

假設(shè)序列w_1, w_2, \ldots, w_T中的每個(gè)詞是依次生成的姆钉,我們有

\begin{align*} P(w_1, w_2, \ldots, w_T) &= \prod_{t=1}^T P(w_t \mid w_1, \ldots, w_{t-1})\\ &= P(w_1)P(w_2 \mid w_1) \cdots P(w_T \mid w_1w_2\cdots w_{T-1}) \end{align*}

例如,一段含有4個(gè)詞的文本序列的概率

P(w_1, w_2, w_3, w_4) = P(w_1) P(w_2 \mid w_1) P(w_3 \mid w_1, w_2) P(w_4 \mid w_1, w_2, w_3).

語言模型的參數(shù)就是詞的概率以及給定前幾個(gè)詞情況下的條件概率抄瓦。設(shè)訓(xùn)練數(shù)據(jù)集為一個(gè)大型文本語料庫潮瓶,如維基百科的所有條目,詞的概率可以通過該詞在訓(xùn)練數(shù)據(jù)集中的相對詞頻來計(jì)算钙姊,例如毯辅,w_1的概率可以計(jì)算為:

\hat P(w_1) = \frac{n(w_1)}{n}

其中n(w_1)為語料庫中以w_1作為第一個(gè)詞的文本的數(shù)量,n為語料庫中文本的總數(shù)量煞额。

類似的悉罕,給定w_1情況下,w_2的條件概率可以計(jì)算為:

\hat P(w_2 \mid w_1) = \frac{n(w_1, w_2)}{n(w_1)}

其中n(w_1, w_2)為語料庫中以w_1作為第一個(gè)詞立镶,w_2作為第二個(gè)詞的文本的數(shù)量。

n元語法

序列長度增加类早,計(jì)算和存儲(chǔ)多個(gè)詞共同出現(xiàn)的概率的復(fù)雜度會(huì)呈指數(shù)級增加媚媒。n元語法通過馬爾可夫假設(shè)簡化模型,馬爾科夫假設(shè)是指一個(gè)詞的出現(xiàn)只與前面n個(gè)詞相關(guān)涩僻,即n階馬爾可夫鏈(Markov chain of order n)缭召,如果n=1栈顷,那么有P(w_3 \mid w_1, w_2) = P(w_3 \mid w_2)∏断铮基于n-1階馬爾可夫鏈萄凤,我們可以將語言模型改寫為

P(w_1, w_2, \ldots, w_T) = \prod_{t=1}^T P(w_t \mid w_{t-(n-1)}, \ldots, w_{t-1}) .

以上也叫n元語法(n-grams),它是基于n - 1階馬爾可夫鏈的概率語言模型搪哪。例如靡努,當(dāng)n=2時(shí),含有4個(gè)詞的文本序列的概率就可以改寫為:

\begin{align*} P(w_1, w_2, w_3, w_4) &= P(w_1) P(w_2 \mid w_1) P(w_3 \mid w_1, w_2) P(w_4 \mid w_1, w_2, w_3)\\ &= P(w_1) P(w_2 \mid w_1) P(w_3 \mid w_2) P(w_4 \mid w_3) \end{align*}

當(dāng)n分別為1晓折、2和3時(shí)惑朦,我們將其分別稱作一元語法(unigram)、二元語法(bigram)和三元語法(trigram)漓概。例如漾月,長度為4的序列w_1, w_2, w_3, w_4在一元語法、二元語法和三元語法中的概率分別為

\begin{aligned} P(w_1, w_2, w_3, w_4) &= P(w_1) P(w_2) P(w_3) P(w_4) ,\\ P(w_1, w_2, w_3, w_4) &= P(w_1) P(w_2 \mid w_1) P(w_3 \mid w_2) P(w_4 \mid w_3) ,\\ P(w_1, w_2, w_3, w_4) &= P(w_1) P(w_2 \mid w_1) P(w_3 \mid w_1, w_2) P(w_4 \mid w_2, w_3) . \end{aligned}

當(dāng)n較小時(shí)胃珍,n元語法往往并不準(zhǔn)確梁肿。例如,在一元語法中觅彰,由三個(gè)詞組成的句子“你走先”和“你先走”的概率是一樣的吩蔑。然而,當(dāng)n較大時(shí)缔莲,n元語法需要計(jì)算并存儲(chǔ)大量的詞頻和多詞相鄰頻率哥纫。

時(shí)序數(shù)據(jù)的采樣

在訓(xùn)練中我們需要每次隨機(jī)讀取小批量樣本和標(biāo)簽。與之前章節(jié)的實(shí)驗(yàn)數(shù)據(jù)不同的是痴奏,時(shí)序數(shù)據(jù)的一個(gè)樣本通常包含連續(xù)的字符蛀骇。假設(shè)時(shí)間步數(shù)為5,樣本序列為5個(gè)字符读拆,即“想”“要”“有”“直”“升”擅憔。該樣本的標(biāo)簽序列為這些字符分別在訓(xùn)練集中的下一個(gè)字符,即“要”“有”“直”“升”“機(jī)”檐晕,即X=“想要有直升”暑诸,Y=“要有直升機(jī)”。

現(xiàn)在我們考慮序列“想要有直升機(jī)辟灰,想要和你飛到宇宙去”个榕,如果時(shí)間步數(shù)為5,有以下可能的樣本和標(biāo)簽:

  • X:“想要有直升”芥喇,Y:“要有直升機(jī)”
  • X:“要有直升機(jī)”西采,Y:“有直升機(jī),”
  • X:“有直升機(jī)继控,”械馆,Y:“直升機(jī)胖眷,想”
  • ...
  • X:“要和你飛到”,Y:“和你飛到宇”
  • X:“和你飛到宇”霹崎,Y:“你飛到宇宙”
  • X:“你飛到宇宙”珊搀,Y:“飛到宇宙去”

可以看到,如果序列的長度為T尾菇,時(shí)間步數(shù)為n境析,那么一共有T-n個(gè)合法的樣本,但是這些樣本有大量的重合错沽,我們通常采用更加高效的采樣方式簿晓。我們有兩種方式對時(shí)序數(shù)據(jù)進(jìn)行采樣,分別是隨機(jī)采樣和相鄰采樣千埃。

隨機(jī)采樣

下面的代碼每次從數(shù)據(jù)里隨機(jī)采樣一個(gè)小批量憔儿。其中批量大小batch_size是每個(gè)小批量的樣本數(shù),num_steps是每個(gè)樣本所包含的時(shí)間步數(shù)放可。
在隨機(jī)采樣中谒臼,每個(gè)樣本是原始序列上任意截取的一段序列,相鄰的兩個(gè)隨機(jī)小批量在原始序列上的位置不一定相毗鄰耀里。

import torch
import random
def data_iter_random(corpus_indices, batch_size, num_steps, device=None):
    # 減1是因?yàn)閷τ陂L度為n的序列蜈缤,X最多只有包含其中的前n - 1個(gè)字符
    num_examples = (len(corpus_indices) - 1) // num_steps  # 下取整,得到不重疊情況下的樣本個(gè)數(shù)
    example_indices = [i * num_steps for i in range(num_examples)]  # 每個(gè)樣本的第一個(gè)字符在corpus_indices中的下標(biāo)
    random.shuffle(example_indices)

    def _data(i):
        # 返回從i開始的長為num_steps的序列
        return corpus_indices[i: i + num_steps]
    if device is None:
        device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    
    for i in range(0, num_examples, batch_size):
        # 每次選出batch_size個(gè)隨機(jī)樣本
        batch_indices = example_indices[i: i + batch_size]  # 當(dāng)前batch的各個(gè)樣本的首字符的下標(biāo)
        X = [_data(j) for j in batch_indices]
        Y = [_data(j + 1) for j in batch_indices]
        yield torch.tensor(X, device=device), torch.tensor(Y, device=device)

測試一下這個(gè)函數(shù)冯挎,我們輸入從0到29的連續(xù)整數(shù)作為一個(gè)人工序列底哥,設(shè)批量大小和時(shí)間步數(shù)分別為2和6,打印隨機(jī)采樣每次讀取的小批量樣本的輸入X和標(biāo)簽Y房官。

my_seq = list(range(30))
for X, Y in data_iter_random(my_seq, batch_size=2, num_steps=6):
    print('X: ', X, '\nY:', Y, '\n')

相鄰采樣

在相鄰采樣中趾徽,相鄰的兩個(gè)隨機(jī)小批量在原始序列上的位置相毗鄰。

def data_iter_consecutive(corpus_indices, batch_size, num_steps, device=None):
    if device is None:
        device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    corpus_len = len(corpus_indices) // batch_size * batch_size  # 保留下來的序列的長度
    corpus_indices = corpus_indices[: corpus_len]  # 僅保留前corpus_len個(gè)字符
    indices = torch.tensor(corpus_indices, device=device)
    indices = indices.view(batch_size, -1)  # resize成(batch_size, )
    batch_num = (indices.shape[1] - 1) // num_steps
    for i in range(batch_num):
        i = i * num_steps
        X = indices[:, i: i + num_steps]
        Y = indices[:, i + 1: i + num_steps + 1]
        yield X, Y

同樣的設(shè)置下翰守,打印相鄰采樣每次讀取的小批量樣本的輸入X和標(biāo)簽Y孵奶。相鄰的兩個(gè)隨機(jī)小批量在原始序列上的位置相毗鄰。

for X, Y in data_iter_consecutive(my_seq, batch_size=2, num_steps=6):
    print('X: ', X, '\nY:', Y, '\n')

GRU?控循環(huán)神經(jīng)?絡(luò)

RNN存在的問題:梯度較容易出現(xiàn)衰減或爆炸(BPTT)
?控循環(huán)神經(jīng)?絡(luò):捕捉時(shí)間序列中時(shí)間步距離較?的依賴關(guān)系
RNN:

image

H_{t} = ?(X_{t}W_{xh} + H_{t-1}W_{hh} + b_{h})
GRU:

R_{t} = σ(X_tW_{xr} + H_{t?1}W_{hr} + b_r)\\ Z_{t} = σ(X_tW_{xz} + H_{t?1}W_{hz} + b_z)\\ \widetilde{H}_t = tanh(X_tW_{xh} + (R_t ⊙H_{t?1})W_{hh} + b_h)\\ H_t = Z_t⊙H_{t?1} + (1?Z_t)⊙\widetilde{H}_t
? 重置?有助于捕捉時(shí)間序列?短期的依賴關(guān)系蜡峰;
? 更新?有助于捕捉時(shí)間序列??期的依賴關(guān)系了袁。

GRU模型
def gru(inputs, state, params):
    W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q = params
    H, = state
    outputs = []
    for X in inputs:
        Z = torch.sigmoid(torch.matmul(X, W_xz) + torch.matmul(H, W_hz) + b_z)
        R = torch.sigmoid(torch.matmul(X, W_xr) + torch.matmul(H, W_hr) + b_r)
        H_tilda = torch.tanh(torch.matmul(X, W_xh) + R * torch.matmul(H, W_hh) + b_h)
        H = Z * H + (1 - Z) * H_tilda
        Y = torch.matmul(H, W_hq) + b_q
        outputs.append(Y)
    return outputs, (H,)
訓(xùn)練模型
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開']
d2l.train_and_predict_rnn(gru, get_params, init_gru_state, num_hiddens,
                          vocab_size, device, corpus_indices, idx_to_char,
                          char_to_idx, False, num_epochs, num_steps, lr,
                          clipping_theta, batch_size, pred_period, pred_len,
                          prefixes)

LSTM

** 長短期記憶long short-term memory **:
遺忘門:控制上一時(shí)間步的記憶細(xì)胞
輸入門:控制當(dāng)前時(shí)間步的輸入
輸出門:控制從記憶細(xì)胞到隱藏狀態(tài)
記憶細(xì)胞:?種特殊的隱藏狀態(tài)的信息的流動(dòng)

I_t = σ(X_tW_{xi} + H_{t?1}W_{hi} + b_i) \\ F_t = σ(X_tW_{xf} + H_{t?1}W_{hf} + b_f)\\ O_t = σ(X_tW_{xo} + H_{t?1}W_{ho} + b_o)\\ \widetilde{C}_t = tanh(X_tW_{xc} + H_{t?1}W_{hc} + b_c)\\ C_t = F_t ⊙C_{t?1} + I_t ⊙\widetilde{C}_t\\ H_t = O_t⊙tanh(C_t)

初始化參數(shù)
num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
print('will use', device)

def get_params():
    def _one(shape):
        ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32)
        return torch.nn.Parameter(ts, requires_grad=True)
    def _three():
        return (_one((num_inputs, num_hiddens)),
                _one((num_hiddens, num_hiddens)),
                torch.nn.Parameter(torch.zeros(num_hiddens, device=device, dtype=torch.float32), requires_grad=True))
    
    W_xi, W_hi, b_i = _three()  # 輸入門參數(shù)
    W_xf, W_hf, b_f = _three()  # 遺忘門參數(shù)
    W_xo, W_ho, b_o = _three()  # 輸出門參數(shù)
    W_xc, W_hc, b_c = _three()  # 候選記憶細(xì)胞參數(shù)
    
    # 輸出層參數(shù)
    W_hq = _one((num_hiddens, num_outputs))
    b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, dtype=torch.float32), requires_grad=True)
    return nn.ParameterList([W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q])

def init_lstm_state(batch_size, num_hiddens, device):
    return (torch.zeros((batch_size, num_hiddens), device=device), 
            torch.zeros((batch_size, num_hiddens), device=device))
LSTM模型
def lstm(inputs, state, params):
    [W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q] = params
    (H, C) = state
    outputs = []
    for X in inputs:
        I = torch.sigmoid(torch.matmul(X, W_xi) + torch.matmul(H, W_hi) + b_i)
        F = torch.sigmoid(torch.matmul(X, W_xf) + torch.matmul(H, W_hf) + b_f)
        O = torch.sigmoid(torch.matmul(X, W_xo) + torch.matmul(H, W_ho) + b_o)
        C_tilda = torch.tanh(torch.matmul(X, W_xc) + torch.matmul(H, W_hc) + b_c)
        C = F * C + I * C_tilda
        H = O * C.tanh()
        Y = torch.matmul(H, W_hq) + b_q
        outputs.append(Y)
    return outputs, (H, C)
訓(xùn)練模型
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開']

d2l.train_and_predict_rnn(lstm, get_params, init_lstm_state, num_hiddens,
                          vocab_size, device, corpus_indices, idx_to_char,
                          char_to_idx, False, num_epochs, num_steps, lr,
                          clipping_theta, batch_size, pred_period, pred_len,
                          prefixes)

深度循環(huán)神經(jīng)網(wǎng)絡(luò)

\boldsymbol{H}_t^{(1)} = \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh}^{(1)} + \boldsymbol{H}_{t-1}^{(1)} \boldsymbol{W}_{hh}^{(1)} + \boldsymbol_h^{(1)})\\ \boldsymbol{H}_t^{(\ell)} = \phi(\boldsymbol{H}_t^{(\ell-1)} \boldsymbol{W}_{xh}^{(\ell)} + \boldsymbol{H}_{t-1}^{(\ell)} \boldsymbol{W}_{hh}^{(\ell)} + \boldsymbol湿颅_h^{(\ell)})\\ \boldsymbol{O}_t = \boldsymbol{H}_t^{(L)} \boldsymbol{W}_{hq} + \boldsymbol载绿_q

num_hiddens=256
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開']

lr = 1e-2 # 注意調(diào)整學(xué)習(xí)率

gru_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens,num_layers=2)
model = d2l.RNNModel(gru_layer, vocab_size).to(device)
d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
                                corpus_indices, idx_to_char, char_to_idx,
                                num_epochs, num_steps, lr, clipping_theta,
                                batch_size, pred_period, pred_len, prefixes)

雙向循環(huán)神經(jīng)網(wǎng)絡(luò)

\begin{aligned} \overrightarrow{\boldsymbol{H}}_t &= \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh}^{(f)} + \overrightarrow{\boldsymbol{H}}_{t-1} \boldsymbol{W}_{hh}^{(f)} + \boldsymbol_h^{(f)})\\ \overleftarrow{\boldsymbol{H}}_t &= \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh}^{(b)} + \overleftarrow{\boldsymbol{H}}_{t+1} \boldsymbol{W}_{hh}^{(b)} + \boldsymbol油航_h^{(b)}) \end{aligned}
\boldsymbol{H}_t=(\overrightarrow{\boldsymbol{H}}_{t}, \overleftarrow{\boldsymbol{H}}_t)
\boldsymbol{O}_t = \boldsymbol{H}_t \boldsymbol{W}_{hq} + \boldsymbol卢鹦_q

num_hiddens=128
num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e-2, 1e-2
pred_period, pred_len, prefixes = 40, 50, ['分開', '不分開']

lr = 1e-2 # 注意調(diào)整學(xué)習(xí)率

gru_layer = nn.GRU(input_size=vocab_size, hidden_size=num_hiddens,bidirectional=True)
model = d2l.RNNModel(gru_layer, vocab_size).to(device)
d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
                                corpus_indices, idx_to_char, char_to_idx,
                                num_epochs, num_steps, lr, clipping_theta,
                                batch_size, pred_period, pred_len, prefixes)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子冀自,更是在濱河造成了極大的恐慌,老刑警劉巖秒啦,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件熬粗,死亡現(xiàn)場離奇詭異,居然都是意外死亡余境,警方通過查閱死者的電腦和手機(jī)驻呐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芳来,“玉大人含末,你說我怎么就攤上這事〖瓷啵” “怎么了佣盒?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長顽聂。 經(jīng)常有香客問我肥惭,道長,這世上最難降的妖魔是什么紊搪? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任蜜葱,我火速辦了婚禮,結(jié)果婚禮上耀石,老公的妹妹穿的比我還像新娘牵囤。我一直安慰自己,他們只是感情好滞伟,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布揭鳞。 她就那樣靜靜地躺著,像睡著了一般诗良。 火紅的嫁衣襯著肌膚如雪汹桦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天鉴裹,我揣著相機(jī)與錄音舞骆,去河邊找鬼。 笑死径荔,一個(gè)胖子當(dāng)著我的面吹牛督禽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播总处,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼狈惫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起胧谈,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤忆肾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后菱肖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體客冈,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年稳强,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了场仲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡退疫,死狀恐怖渠缕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情褒繁,我是刑警寧澤亦鳞,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站澜汤,受9級特大地震影響蚜迅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜俊抵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一谁不、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧徽诲,春花似錦刹帕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至钱贯,卻和暖如春挫掏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背秩命。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工尉共, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人弃锐。 一個(gè)月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓袄友,卻偏偏與公主長得像,于是被迫代替她去往敵國和親霹菊。 傳聞我的和親對象是個(gè)殘疾皇子剧蚣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348