Coggle 30 Days of ML(21年11月)Pytorch与视觉竞赛入门

二.Pytorch与视觉竞赛入门

任务列表

任务1:PyTorch张量计算与Numpy的转换

任务要点:Pytorch基础使用、张量计算

c = np.ones((3,3))
d = torch.from_numpy(c)  #numpy 转tensor

解答:

import torch
import numpy as np

c = np.ones((3,3))
d = torch.from_numpy(c)  #numpy转tensor
e = d.numpy()  #tensor转numpy
d,e

任务2:梯度计算和梯度下降过程

任务要点:Pytorch梯度计算、随机梯度下降

解答:
一定要看清要求!!
由题意我觉得用nn.Linear()不合适,因为没有用到要去定义的w和b。我们nn.Parameter()初始化参数,介绍参考https://blog.csdn.net/qq_28753373/article/details/104179354
还有个坑就是这里用SGD一定要调小一点,不然会梯度爆炸。用Adam则不会出现问题。

import numpy as np
import torch.nn as nn
import torch
import torch.optim as optim

class MLP(nn.Module):

    def __init__(self, w, b):
        super(MLP, self).__init__()
        self.weight = nn.Parameter(w)  # 使用nn.Parameter()对weights进行初始化
        self.bias = nn.Parameter(b)

    def forward(self, x):
        out = torch.matmul(x, self.weight) + self.bias
        return out

w = torch.tensor([[1.]])
b = torch.tensor([1.])
x = np.arange(0, 100, 0.01)
noise = np.random.normal(0, 1, 10000)  # (0,1)的高斯噪声
y = 10 * x + 4 + noise

x = torch.from_numpy(x).float()
y = torch.from_numpy(y).float()  # .float
x = torch.unsqueeze(x, dim=1)  # 转换[1,10000]为[10000,1]
y = torch.unsqueeze(y, dim=1)  # 转换[1,10000]为[10000,1]

model = MLP(w, b)
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.0001)  # 一定是小的学习率,不然会梯度爆炸

for epoch in range(100000):
    y_pre = model(x)
    loss = criterion(y_pre, y)
    if epoch % 1000 == 0:
        print("Epoch:{}, loss is {}".format(epoch, loss))

    optimizer.zero_grad()  # 梯度清零
    loss.backward()  # 反向传播计算梯度
    optimizer.step()  # 更新参数

print("w的值为", model.weight.item())
print("b的值为", model.bias.item())

任务3:PyTorch全连接层原理和使用

任务要点:全连接网络
步骤1:学习全连接网络原理,https://blog.csdn.net/xiaodong_11/article/details/82015456

步骤2:在pytorch中使用矩阵乘法实现全连接层

步骤3:在pytorch中使用nn.Linear层

解答:

import torch
import torch.nn as nn

class Mylinear(nn.Module):
    def __init__(self, in_features, out_features):
        super(Mylinear, self).__init__()
        self.weight = nn.Parameter(torch.Tensor(out_features, in_features))
        self.bias = nn.Parameter(torch.Tensor(out_features))

    def forward(self, x):
        out = x@self.weight.t()+self.bias
        return out

model = Mylinear(784, 10)
x = torch.rand(100, 784)
out = model(x)

for name, param in model.named_parameters():
    print('%14s : %s' % (name, param.shape))  # 打印参数名和参数数量
    # print('%s' % param)  # 可以打印出参数
import torch
import torch.nn as nn


class linear(nn.Module):
    def __init__(self, in_features, out_features):
        super(linear, self).__init__()
        self.fc = nn.Linear(in_features, out_features)

    def forward(self, x):
        out = self.fc(x)
        return out


model = linear(784, 10)
x = torch.rand(100, 784)
out = model(x)

for name, param in model.named_parameters():
    print('%14s : %s' % (name, param.shape))  # 打印参数名和参数数量
    # print('%s' % param)  # 可以打印出参数

任务4:PyTorch激活函数原理和使用

任务要点:激活函数

步骤1:学习激活函数的原理,https://zhuanlan.zhihu.com/p/88429934

步骤2:在pytorch中手动实现上述激活函数

解答:

def ELU(x, alpha=1.0, inplace=False):
    return max(0, x) + min(0, alpha * (np.exp(x) - 1))


def LeakyReLU(x, negative_slope=0.01, inplace=False):
    return max(0, x) + negative_slope * min(0, x)


def PReLU(x, num_parameters=1, init=0.25):
    return max(0, x) + init * min(0, x)


def ReLU(x, inplace=False):
    return max(0, x)


def ReLU6(x, inplace=False):
    return min(max(0, x), 6)


def SELU(x,  inplace=False):
    alpha=1.6732632423543772848170429916717
    scale=1.0507009873554804934193349852946
    return scale * (max(0, x) + min(0, alpha * (np.exp(x) - 1)))


def CELU(x, alpha=1.00, inplace=False):
    return max(0, x) + min(0, alpha * (np.exp(x / alpha) - 1))


def Sigmoid(x):
    return 1.0 / (1.0 + np.exp(-x))


def LogSigmoid(x):
    return np.log(1.0 / (1.0 + np.exp(-x)))


def Tanh(x):
    return np.exp(x) - np.exp(-x) / np.exp(x) + np.exp(-x)


def TanhShrink(x):
    return x - (np.exp(x) - np.exp(-x) / np.exp(x) + np.exp(-x))


def Softplus(x, beta=1, threshold=20):
    return (1.0 / beta) * (np.log(1 + np.exp(beta * x)))


def SoftShrink(x, lambd=0.5):
    if x > lambd:
        return x - lambd
    elif x < -lambd:
        return x + lambd
    return 0

任务5:PyTorch卷积层原理和使用

任务要点:卷积层

步骤1:理解卷积层的原理和具体使用

https://blog.csdn.net/qq_37385726/article/details/81739179
https://www.cnblogs.com/zhangxiann/p/13584415.html

步骤2:计算下如下卷积层的参数量

nn.Conv2d(            
        in_channels=1,            
        out_channels=32,            
        kernel_size=5,            
        stride=1,            
        padding=2
)

解答:
方法一:函数计算

import torch.nn as nn
import torch  

class net(nn.Module):
    def __init__(self):
        super(net, self).__init__()
        self.conv1 = nn.Conv2d(
            in_channels=1,
            out_channels=32,
            kernel_size=5,
            stride=1,
            padding=2
        )

    def forward(self, x):
        return self.conv1(x)

model = net()
p = sum(map(lambda p: p.numel(), model.parameters()))
print(p)


方法二:直接计算
计算公式

in_channel是1,1个kernel的参数是5x5x1,32个out_channel也就是32个卷积核,bias也就是32,5x5x1x32+32=832

任务6:PyTorch常见的损失函数和优化器使用

任务要点:损失函数、优化器
步骤1:学习损失函数的细节,https://www.cnblogs.com/wanghui-garcia/p/10862733.html
步骤2:学习优化器的使用,https://pytorch.org/docs/stable/optim.html
步骤3:设置不同的优化器和学习率,重复任务2的回归过程

  • 损失函数MSE、优化器SGD、学习率0.1
  • 损失函数MSE、优化器SGD、学习率0.5
  • 损失函数MSE、优化器SGD、学习率0.01

解答:
这里我们重新构造x,y, w,b, noise,方便用SGD优化。如果用任务2的数据,结果惨不忍睹(因为lr太大,第二个epoch都是nan了)

import torch
import torch.nn as nn
import torch.optim as optim

class linear(nn.Module):
    def __init__(self, in_features, out_features):
        super(linear, self).__init__()
        self.fc = nn.Linear(in_features, out_features)

    def forward(self, x):
        out = self.fc(x)
        return out


x = torch.randn(4, 3)
w = torch.randint(5, 10, size=(3, 1), dtype=torch.float)
b = torch.tensor(5.)
noise = torch.randn(4, 1)
y = x @ w + b + noise
res = {}

for lr in [0.5, 0.1, 0.01]:
    best_loss = float("inf")
    best_epoch = 0
    model = linear(3, 1)
    criterion = nn.MSELoss()
    optimizer = optim.SGD(model.parameters(), lr=lr)  
    for epoch in range(100000):
        y_pre = model(x)
        loss = criterion(y_pre, y)
        if epoch % 1000 == 0:
            print("Epoch:{}, loss is {}".format(epoch, loss))

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        if loss < best_loss:
            best_loss = loss
            best_epoch = epoch
    res[lr] = {'loss': best_loss.item(), 'epoch': best_epoch}

print(res)

任务7:PyTorch池化层和归一化层

任务要点:池化层、归一化层

解答:

import torch
import torch.nn as nn

x = torch.randn(10, 3, 32, 32)
avg = nn.AvgPool2d(3, 3)
print(avg(x).shape)

maxp = nn.MaxPool2d(7, 3)
print(maxp(x).shape)

输出为:
torch.Size([10, 3, 10, 10])
torch.Size([10, 3, 9, 9])

x = torch.randint(10, size=(10, 3, 32, 32)).float()

bn = nn.BatchNorm2d(3)
gn = nn.GroupNorm(num_groups=1, num_channels=3)   # num_channels必须被num_groups整除

任务8:使用PyTorch搭建VGG网络

任务要点:网络搭建

https://zhuanlan.zhihu.com/p/263527295

  • 步骤1:理解VGG网络的原理。

  • 步骤2:使用pytorch搭建VGG网络模型。

  • 步骤3:打印出VGG 11层模型 每层特征图的尺寸,以及参数量。

解答:

# -*- coding: UTF-8 -*-
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchsummary import summary


class VGG(nn.Module):
    """
    VGG builder
    """

    def __init__(self, arch: object, num_classes=1000) -> object:
        super(VGG, self).__init__()
        self.in_channels = 3
        self.conv3_64 = self.__make_layer(64, arch[0])
        self.conv3_128 = self.__make_layer(128, arch[1])
        self.conv3_256 = self.__make_layer(256, arch[2])
        self.conv3_512a = self.__make_layer(512, arch[3])
        self.conv3_512b = self.__make_layer(512, arch[4])
        self.fc1 = nn.Linear(7 * 7 * 512, 4096)
        self.bn1 = nn.BatchNorm1d(4096)
        self.bn2 = nn.BatchNorm1d(4096)
        self.fc2 = nn.Linear(4096, 4096)
        self.fc3 = nn.Linear(4096, num_classes)

    def __make_layer(self, channels, num):
        layers = []
        for i in range(num):
            layers.append(nn.Conv2d(self.in_channels, channels, 3, stride=1, padding=1, bias=False))  # same padding
            layers.append(nn.BatchNorm2d(channels))
            layers.append(nn.ReLU())
            self.in_channels = channels
        return nn.Sequential(*layers)

    def forward(self, x):
        out = self.conv3_64(x)
        out = F.max_pool2d(out, 2)
        out = self.conv3_128(out)
        out = F.max_pool2d(out, 2)
        out = self.conv3_256(out)
        out = F.max_pool2d(out, 2)
        out = self.conv3_512a(out)
        out = F.max_pool2d(out, 2)
        out = self.conv3_512b(out)
        out = F.max_pool2d(out, 2)
        out = out.view(out.size(0), -1)
        out = self.fc1(out)
        out = self.bn1(out)
        out = F.relu(out)
        out = self.fc2(out)
        out = self.bn2(out)
        out = F.relu(out)
        return self.fc3(out)


def VGG_11():
    return VGG([1, 1, 2, 2, 2], num_classes=1000)


def VGG_13():
    return VGG([1, 1, 2, 2, 2], num_classes=1000)


def VGG_16():
    return VGG([2, 2, 3, 3, 3], num_classes=1000)


def VGG_19():
    return VGG([2, 2, 4, 4, 4], num_classes=1000)


def test():
    net = VGG_11()
    # net = VGG_13()
    # net = VGG_16()
    # net = VGG_19()
    summary(net, (3, 224, 224), device="cpu")


test()

输出

----------------------------------------------------------------
        Layer (type)               Output Shape         Param #
================================================================
            Conv2d-1         [-1, 64, 224, 224]           1,728
       BatchNorm2d-2         [-1, 64, 224, 224]             128
              ReLU-3         [-1, 64, 224, 224]               0
            Conv2d-4        [-1, 128, 112, 112]          73,728
       BatchNorm2d-5        [-1, 128, 112, 112]             256
              ReLU-6        [-1, 128, 112, 112]               0
            Conv2d-7          [-1, 256, 56, 56]         294,912
       BatchNorm2d-8          [-1, 256, 56, 56]             512
              ReLU-9          [-1, 256, 56, 56]               0
           Conv2d-10          [-1, 256, 56, 56]         589,824
      BatchNorm2d-11          [-1, 256, 56, 56]             512
             ReLU-12          [-1, 256, 56, 56]               0
           Conv2d-13          [-1, 512, 28, 28]       1,179,648
      BatchNorm2d-14          [-1, 512, 28, 28]           1,024
             ReLU-15          [-1, 512, 28, 28]               0
           Conv2d-16          [-1, 512, 28, 28]       2,359,296
      BatchNorm2d-17          [-1, 512, 28, 28]           1,024
             ReLU-18          [-1, 512, 28, 28]               0
           Conv2d-19          [-1, 512, 14, 14]       2,359,296
      BatchNorm2d-20          [-1, 512, 14, 14]           1,024
             ReLU-21          [-1, 512, 14, 14]               0
           Conv2d-22          [-1, 512, 14, 14]       2,359,296
      BatchNorm2d-23          [-1, 512, 14, 14]           1,024
             ReLU-24          [-1, 512, 14, 14]               0
           Linear-25                 [-1, 4096]     102,764,544
      BatchNorm1d-26                 [-1, 4096]           8,192
           Linear-27                 [-1, 4096]      16,781,312
      BatchNorm1d-28                 [-1, 4096]           8,192
           Linear-29                 [-1, 1000]       4,097,000
================================================================
Total params: 132,882,472
Trainable params: 132,882,472
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.57
Forward/backward pass size (MB): 170.10
Params size (MB): 506.91
Estimated Total Size (MB): 677.58
----------------------------------------------------------------

任务9:使用PyTorch搭建ResNet网络

任务要点:网络搭建

https://zhuanlan.zhihu.com/p/263526658

  • 步骤1:理解ResNet网络的原理。

  • 步骤2:使用pytorch搭建ResNet网络模型。

  • 步骤3:打印出ResNet 18模型 每层特征图的尺寸,以及参数量。

解答:

# -*- coding: UTF-8 -*-
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchsummary import summary


class SE(nn.Module):

    def __init__(self, in_chnls, ratio):
        super(SE, self).__init__()
        self.squeeze = nn.AdaptiveAvgPool2d((1, 1))
        self.compress = nn.Conv2d(in_chnls, in_chnls // ratio, 1, 1, 0)
        self.excitation = nn.Conv2d(in_chnls // ratio, in_chnls, 1, 1, 0)

    def forward(self, x):
        out = self.squeeze(x)
        out = self.compress(out)
        out = F.relu(out)
        out = self.excitation(out)
        return F.sigmoid(out)


class BN_Conv2d(nn.Module):
    """
    BN_CONV, default activation is ReLU
    """

    def __init__(self, in_channels: object, out_channels: object, kernel_size: object, stride: object, padding: object,
                 dilation=1, groups=1, bias=False, activation=True) -> object:
        super(BN_Conv2d, self).__init__()
        layers = [nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size, stride=stride,
                            padding=padding, dilation=dilation, groups=groups, bias=bias),
                  nn.BatchNorm2d(out_channels)]
        if activation:
            layers.append(nn.ReLU(inplace=True))
        self.seq = nn.Sequential(*layers)

    def forward(self, x):
        return self.seq(x)


class BasicBlock(nn.Module):
    """
    basic building block for ResNet-18, ResNet-34
    """
    message = "basic"

    def __init__(self, in_channels, out_channels, strides, is_se=False):
        super(BasicBlock, self).__init__()
        self.is_se = is_se
        self.conv1 = BN_Conv2d(in_channels, out_channels, 3, stride=strides, padding=1, bias=False)  # same padding
        self.conv2 = BN_Conv2d(out_channels, out_channels, 3, stride=1, padding=1, bias=False, activation=False)
        if self.is_se:
            self.se = SE(out_channels, 16)

        # fit input with residual output
        self.short_cut = nn.Sequential()
        if strides is not 1:
            self.short_cut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, 1, stride=strides, padding=0, bias=False),
                nn.BatchNorm2d(out_channels)
            )

    def forward(self, x):
        out = self.conv1(x)
        out = self.conv2(out)
        if self.is_se:
            coefficient = self.se(out)
            out = out * coefficient
        out = out + self.short_cut(x)
        return F.relu(out)


class ResNet(nn.Module):
    """
    building ResNet_34
    """

    def __init__(self, block: object, groups: object, num_classes=1000) -> object:
        super(ResNet, self).__init__()
        self.channels = 64  # out channels from the first convolutional layer
        self.block = block

        self.conv1 = nn.Conv2d(3, self.channels, 7, stride=2, padding=3, bias=False)
        self.bn = nn.BatchNorm2d(self.channels)
        self.pool1 = nn.MaxPool2d(3, 2, 1)
        self.conv2_x = self._make_conv_x(channels=64, blocks=groups[0], strides=1, index=2)
        self.conv3_x = self._make_conv_x(channels=128, blocks=groups[1], strides=2, index=3)
        self.conv4_x = self._make_conv_x(channels=256, blocks=groups[2], strides=2, index=4)
        self.conv5_x = self._make_conv_x(channels=512, blocks=groups[3], strides=2, index=5)
        self.pool2 = nn.AvgPool2d(7)
        patches = 512 if self.block.message == "basic" else 512 * 4
        self.fc = nn.Linear(patches, num_classes)  # for 224 * 224 input size

    def _make_conv_x(self, channels, blocks, strides, index):
        """
        making convolutional group
        :param channels: output channels of the conv-group
        :param blocks: number of blocks in the conv-group
        :param strides: strides
        :return: conv-group
        """
        list_strides = [strides] + [1] * (blocks - 1)  # In conv_x groups, the first strides is 2, the others are ones.
        conv_x = nn.Sequential()
        for i in range(len(list_strides)):
            layer_name = str("block_%d_%d" % (index, i))  # when use add_module, the name should be difference.
            conv_x.add_module(layer_name, self.block(self.channels, channels, list_strides[i]))
            self.channels = channels if self.block.message == "basic" else channels * 4
        return conv_x

    def forward(self, x):
        out = self.conv1(x)
        out = F.relu(self.bn(out))
        out = self.pool1(out)
        out = self.conv2_x(out)
        out = self.conv3_x(out)
        out = self.conv4_x(out)
        out = self.conv5_x(out)
        out = self.pool2(out)
        out = out.view(out.size(0), -1)
        out = F.softmax(self.fc(out))
        return out


def ResNet_18(num_classes=1000):
    return ResNet(block=BasicBlock, groups=[2, 2, 2, 2], num_classes=num_classes)


# def ResNet_34(num_classes=1000):
#     return ResNet(block=BasicBlock, groups=[3, 4, 6, 3], num_classes=num_classes)
#
# def ResNet_50(num_classes=1000):
#     return ResNet(block=BottleNeck, groups=[3, 4, 6, 3], num_classes=num_classes)
#
# def ResNet_101(num_classes=1000):
#     return ResNet(block=BottleNeck, groups=[3, 4, 23, 3], num_classes=num_classes)
#
# def ResNet_152(num_classes=1000):
#     return ResNet(block=BottleNeck, groups=[3, 8, 36, 3], num_classes=num_classes)

def test():
    net = ResNet_18()
    # net = ResNet_34()
    # net = ResNet_50()
    # net = ResNet_101()
    # net = ResNet_152()
    summary(net, (3, 224, 224), device='cpu')


test()

输出

----------------------------------------------------------------
        Layer (type)               Output Shape         Param #
================================================================
            Conv2d-1         [-1, 64, 112, 112]           9,408
       BatchNorm2d-2         [-1, 64, 112, 112]             128
         MaxPool2d-3           [-1, 64, 56, 56]               0
            Conv2d-4           [-1, 64, 56, 56]          36,864
       BatchNorm2d-5           [-1, 64, 56, 56]             128
              ReLU-6           [-1, 64, 56, 56]               0
         BN_Conv2d-7           [-1, 64, 56, 56]               0
            Conv2d-8           [-1, 64, 56, 56]          36,864
       BatchNorm2d-9           [-1, 64, 56, 56]             128
        BN_Conv2d-10           [-1, 64, 56, 56]               0
       BasicBlock-11           [-1, 64, 56, 56]               0
           Conv2d-12           [-1, 64, 56, 56]          36,864
      BatchNorm2d-13           [-1, 64, 56, 56]             128
             ReLU-14           [-1, 64, 56, 56]               0
        BN_Conv2d-15           [-1, 64, 56, 56]               0
           Conv2d-16           [-1, 64, 56, 56]          36,864
      BatchNorm2d-17           [-1, 64, 56, 56]             128
        BN_Conv2d-18           [-1, 64, 56, 56]               0
       BasicBlock-19           [-1, 64, 56, 56]               0
           Conv2d-20          [-1, 128, 28, 28]          73,728
      BatchNorm2d-21          [-1, 128, 28, 28]             256
             ReLU-22          [-1, 128, 28, 28]               0
        BN_Conv2d-23          [-1, 128, 28, 28]               0
           Conv2d-24          [-1, 128, 28, 28]         147,456
      BatchNorm2d-25          [-1, 128, 28, 28]             256
        BN_Conv2d-26          [-1, 128, 28, 28]               0
           Conv2d-27          [-1, 128, 28, 28]           8,192
      BatchNorm2d-28          [-1, 128, 28, 28]             256
       BasicBlock-29          [-1, 128, 28, 28]               0
           Conv2d-30          [-1, 128, 28, 28]         147,456
      BatchNorm2d-31          [-1, 128, 28, 28]             256
             ReLU-32          [-1, 128, 28, 28]               0
        BN_Conv2d-33          [-1, 128, 28, 28]               0
           Conv2d-34          [-1, 128, 28, 28]         147,456
      BatchNorm2d-35          [-1, 128, 28, 28]             256
        BN_Conv2d-36          [-1, 128, 28, 28]               0
       BasicBlock-37          [-1, 128, 28, 28]               0
           Conv2d-38          [-1, 256, 14, 14]         294,912
      BatchNorm2d-39          [-1, 256, 14, 14]             512
             ReLU-40          [-1, 256, 14, 14]               0
        BN_Conv2d-41          [-1, 256, 14, 14]               0
           Conv2d-42          [-1, 256, 14, 14]         589,824
      BatchNorm2d-43          [-1, 256, 14, 14]             512
        BN_Conv2d-44          [-1, 256, 14, 14]               0
           Conv2d-45          [-1, 256, 14, 14]          32,768
      BatchNorm2d-46          [-1, 256, 14, 14]             512
       BasicBlock-47          [-1, 256, 14, 14]               0
           Conv2d-48          [-1, 256, 14, 14]         589,824
      BatchNorm2d-49          [-1, 256, 14, 14]             512
             ReLU-50          [-1, 256, 14, 14]               0
        BN_Conv2d-51          [-1, 256, 14, 14]               0
           Conv2d-52          [-1, 256, 14, 14]         589,824
      BatchNorm2d-53          [-1, 256, 14, 14]             512
        BN_Conv2d-54          [-1, 256, 14, 14]               0
       BasicBlock-55          [-1, 256, 14, 14]               0
           Conv2d-56            [-1, 512, 7, 7]       1,179,648
      BatchNorm2d-57            [-1, 512, 7, 7]           1,024
             ReLU-58            [-1, 512, 7, 7]               0
        BN_Conv2d-59            [-1, 512, 7, 7]               0
           Conv2d-60            [-1, 512, 7, 7]       2,359,296
      BatchNorm2d-61            [-1, 512, 7, 7]           1,024
        BN_Conv2d-62            [-1, 512, 7, 7]               0
           Conv2d-63            [-1, 512, 7, 7]         131,072
      BatchNorm2d-64            [-1, 512, 7, 7]           1,024
       BasicBlock-65            [-1, 512, 7, 7]               0
           Conv2d-66            [-1, 512, 7, 7]       2,359,296
      BatchNorm2d-67            [-1, 512, 7, 7]           1,024
             ReLU-68            [-1, 512, 7, 7]               0
        BN_Conv2d-69            [-1, 512, 7, 7]               0
           Conv2d-70            [-1, 512, 7, 7]       2,359,296
      BatchNorm2d-71            [-1, 512, 7, 7]           1,024
        BN_Conv2d-72            [-1, 512, 7, 7]               0
       BasicBlock-73            [-1, 512, 7, 7]               0
        AvgPool2d-74            [-1, 512, 1, 1]               0
           Linear-75                 [-1, 1000]         513,000
================================================================
Total params: 11,689,512
Trainable params: 11,689,512
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.57
Forward/backward pass size (MB): 62.41
Params size (MB): 44.59
Estimated Total Size (MB): 107.58
----------------------------------------------------------------

任务10:使用PyTorch完成Fashion-MNIST分类

https://github.com/masoudrostami/Fashion-MNIST-using-PyTorch/blob/main/MNIST%20Fashion%20Project.ipynb

  • 步骤1:搭建4层卷积 + 2层全连接的分类模型。

  • 步骤2:在训练过程中记录下每个epoch的训练集精度和测试集精度。

解答:

from torch import nn, optim
from torchvision import transforms
import torchvision
import torch
from torch.utils.data import Dataset


# Different  classes in Fashion MNIST dataset
# classes = ('Tshirt', 'Trouser', 'Pullover', 'Dress', 'Coat',
#            'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Anke boot')

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
transform = transforms.Compose([transforms.ToTensor(),
                                transforms.Normalize((0.5,), (0.5,))])

# Download and load the training and test data
trainset = torchvision.datasets.FashionMNIST('data', download=True, train=True, transform=transform)
testset = torchvision.datasets.FashionMNIST('data', download=True, train=False, transform=transform)

trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=True)


class Fashion(nn.Module):
    def __init__(self):
        super(Fashion, self).__init__()
        self.model = nn.Sequential(nn.Conv2d(1, 6, kernel_size=7, stride=1),
                                   nn.Conv2d(6, 16, kernel_size=7, stride=1),
                                   nn.Conv2d(16, 16, kernel_size=7, stride=1),
                                   nn.Conv2d(16, 32, kernel_size=7, stride=1),
                                   nn.Flatten(),
                                   nn.Linear(32 * 4 * 4, 128),
                                   nn.Linear(128, 10),
                                   )

    def forward(self, x):
        out = self.model(x)
        return out


def get_acc(output, label):
    total = output.shape[0]
    _, pred_label = output.max(1)
    num_correct = (pred_label == label).sum().item()
    return num_correct / total


model = Fashion().to(device)
error = nn.NLLLoss().to(device)  
learning_rate = 0.1  
optimizer = optim.Adam(model.parameters(), lr=learning_rate)

num_epochs = 50

for e in range(num_epochs):
    # running_loss = 0
    train_acc = 0
    for images, labels in trainloader:
        images, labels = images.to(device), labels.to(device)
        log_ps = model(images)
        loss = error(log_ps, labels)

        optimizer.zero_grad()  # Zeroing our gradients
        loss.backward()  # Taking  backward pass
        optimizer.step()

        train_acc += get_acc(log_ps, labels)
        # running_loss += loss.item()

    print(f"Training Accuracy: {train_acc / len(trainloader) * 100:.2f}", end=" ")

    model.eval()
    with torch.no_grad():
        total = 0
        correct = 0
        for images, labels in testloader:
            images, labels = images.to(device), labels.to(device)
            log_ps = model(images)
            mx_index = torch.argmax(log_ps, dim=1)
            total += labels.numel()
            correct += sum(mx_index == labels).item()
        print(f"Test Accuracy {correct / total * 100:.2f}")

结果有点莫名其妙,待优化...

任务11:使用PyTorch完成人脸关键点检测

数据集:https://ai-contest-static.xfyun.cn/2021/7afa865e-5ac8-48ab-9966-d88bb33cdc15/%E4%BA%BA%E8%84%B8%E5%85%B3%E9%94%AE%E7%82%B9%E6%A3%80%E6%B5%8B%E6%8C%91%E6%88%98%E8%B5%9B_%E6%95%B0%E6%8D%AE%E9%9B%86.zip

https://gitee.com/coggle/competition-baseline/blob/master/competition/%E7%A7%91%E5%A4%A7%E8%AE%AF%E9%A3%9EAI%E5%BC%80%E5%8F%91%E8%80%85%E5%A4%A7%E8%B5%9B2021/%E4%BA%BA%E8%84%B8%E5%85%B3%E9%94%AE%E7%82%B9%E6%A3%80%E6%B5%8B%E6%8C%91%E6%88%98%E8%B5%9B/face-keypoint2.ipynb

  • 步骤1:搭建4层卷积 + 2层的模型完成关键点回归。

  • 步骤2:使用resnet18预训练模型完成关键点回归。

解答:

import os, sys, codecs, glob
from PIL import Image, ImageDraw
import numpy as np
import pandas as pd
import time
from torchvision.models.resnet import resnet18
import torch
import matplotlib.pyplot as plt
from sklearn.metrics import mean_absolute_error
import timm

torch.backends.cudnn.benchmark = False
import torchvision.models as models
import torchvision.transforms as transforms
import torchvision.datasets as datasets
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable
from torch.utils.data.dataset import Dataset
import warnings

warnings.filterwarnings("ignore")


# 单个样本读取
class load_data(Dataset):
    def __init__(self, img, keypoint, transform=None):
        self.img = img
        self.transform = transform
        self.keypoint = keypoint

    def __getitem__(self, index):
        img = Image.fromarray(self.img[:, :, index]).convert('RGB')

        if self.transform is not None:
            img = self.transform(img)

        return img, self.keypoint[index] / 96.0

    def __len__(self):
        return self.img.shape[-1]


class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.model = nn.Sequential(nn.Conv2d(3, 6, kernel_size=5, stride=2),
                                   nn.Conv2d(6, 16, kernel_size=5, stride=2),
                                   nn.Conv2d(16, 16, kernel_size=5, stride=2),
                                   nn.Conv2d(16, 32, kernel_size=5, stride=2),
                                   nn.Flatten(),
                                   nn.Linear(32 * 3 * 3, 784),
                                   nn.Linear(784, 8),
                                   )

    def forward(self, x):
        out = self.model(x)
        return out


class resNet(nn.Module):
    def __init__(self, model):
        super(resNet, self).__init__()
        self.model = nn.Sequential(*list(model.children())[:-1],  # [b, 512, 1, 1] *是防止Sequential打乱顺序
                                   nn.Flatten(),  # [b, 512, 1, 1] -> [b, 512*1*1]
                                   nn.Linear(512, 8))

    def forward(self, x):
        out = self.model(x)
        return out


class XunFeiNet(nn.Module):
    def __init__(self):
        super(XunFeiNet, self).__init__()
        self.model = timm.create_model('resnet18', num_classes=8, pretrained=True)

    def forward(self, img, labels=None):
        feat = self.model(img)
        return feat


def train(train_loader, model, criterion, optimizer, epoch):
    model.train()

    for i, (input, target) in enumerate(train_loader):
        input = input.cuda(non_blocking=True).float()
        target = target.cuda(non_blocking=True).float()

        output = model(input)
        loss = criterion(output, target)
        optimizer.zero_grad()
        torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
        loss.backward()
        optimizer.step()

        if i % 200 == 0:
            print(loss.item())


def validate(val_loader, model):
    model.eval()

    val_feats = []
    with torch.no_grad():
        end = time.time()
        for i, (input, target) in enumerate(val_loader):
            input = input.cuda().float()
            target = target.cuda().float()
            output = model(input)
            val_feats.append(output.data.cpu().numpy())
    return val_feats


if __name__ == '__main__':
    train_df = pd.read_csv('人脸关键点检测挑战赛_数据集/train.csv')
    train_df = train_df.fillna(48)
    train_img = np.load('人脸关键点检测挑战赛_数据集/train.npy')
    test_img = np.load('人脸关键点检测挑战赛_数据集/test.npy')

    # 单个样本读取 -> 批量样本读取
    train_loader = torch.utils.data.DataLoader(
        load_data(train_img[:, :, :-500], train_df.values[:-500],
                  transforms.Compose([
                      transforms.ToTensor(),
                  ])
                  ),
        batch_size=10, shuffle=True
    )

    val_loader = torch.utils.data.DataLoader(
        load_data(train_img[:, :, -500:], train_df.values[-500:],
                  transforms.Compose([
                      transforms.ToTensor(),
                  ])
                  ),
        batch_size=10, shuffle=False
    )

    test_loader = torch.utils.data.DataLoader(
        load_data(test_img, np.zeros((2049, 8)),
                  transforms.Compose([
                      transforms.ToTensor(),
                  ])
                  ),
        batch_size=10, shuffle=False
    )

    # model = Net().cuda()
    # model = resNet(resnet18(pretrained=True)).cuda()
    model = XunFeiNet().cuda()
    criterion = nn.MSELoss().cuda()
    optimizer = torch.optim.Adam(model.parameters(), 0.001)
    scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.85)
    best_acc = 0.0

    for epoch in range(5):
        print('Epoch: ', epoch)

        train(train_loader, model, criterion, optimizer, epoch)

        val_feats = validate(val_loader, model)
        scheduler.step()

        val_feats = np.vstack(val_feats) * 96
        print('Val', mean_absolute_error(val_feats, train_df.values[-500:]))

    pred_tta = []
    pred = []
    with torch.no_grad():
        for t, (x, y) in enumerate(test_loader):
            x_var = x.cuda()
            y_var = y.cuda()
            scores = model(x_var)
            pred.append(scores.data.cpu().numpy())
    pred = np.concatenate(pred, 0)
    pred_tta.append(pred)

    pred = np.mean(pred_tta, axis=0)

    idx = 409
    xy = pred[idx].reshape(4, 2) * 96
    plt.scatter(xy[:, 0], xy[:, 1], c='r')
    plt.imshow(test_img[:, :, idx], cmap='gray')
    plt.show()

    # col = ['left_eye_center_x', 'left_eye_center_y', 'right_eye_center_x',
    #  'right_eye_center_y', 'nose_tip_x', 'nose_tip_y',
    #  'mouth_center_bottom_lip_x','mouth_center_bottom_lip_y']
    # pd.DataFrame(pred * 96, columns=col).to_csv('submit.csv', index=None)

Net,resNet,XunFeiNet分别指自己搭建的网络,改造的torchvision.models预训练resnet18网络和timm中的预训练resnet18,注意:运行code,使用预训练模型需要等待自动下载完成!
Net结果:


测试一张图片

resNet结果:

测试一张图片

XunFeiNet结果:

测试一张图片

任务12:使用PyTorch搭建对抗生成网络

解答:

from __future__ import print_function
# %matplotlib inline
import random
import torch.backends.cudnn as cudnn
import torch.utils.data
import torchvision.utils as vutils
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython.display import HTML

from PIL import Image, ImageDraw
import numpy as np
import pandas as pd
torch.backends.cudnn.benchmark = False
import torchvision.transforms as transforms
import torch.nn as nn
import torch.optim as optim
from torch.utils.data.dataset import Dataset


class XunFeiDataset(Dataset):
    def __init__(self, img, keypoint, transform=None):
        self.img = img
        self.transform = transform
        self.keypoint = keypoint

    def __getitem__(self, index):
        img = Image.fromarray(self.img[:, :, index]).convert('RGB')

        if self.transform is not None:
            img = self.transform(img)

        return img, self.keypoint[index] / 96.0

    def __len__(self):
        return self.img.shape[-1]


# custom weights initialization called on netG and netD
def weights_init(m):
    classname = m.__class__.__name__
    if classname.find('Conv') != -1:
        nn.init.normal_(m.weight.data, 0.0, 0.02)
    elif classname.find('BatchNorm') != -1:
        nn.init.normal_(m.weight.data, 1.0, 0.02)
        nn.init.constant_(m.bias.data, 0)


# Generator Code
class Generator(nn.Module):
    def __init__(self, ngpu):
        super(Generator, self).__init__()
        self.ngpu = ngpu
        self.main = nn.Sequential(
            # input is Z, going into a convolution
            nn.ConvTranspose2d(nz, ngf * 8, 4, 1, 0, bias=False),
            nn.BatchNorm2d(ngf * 8),
            nn.ReLU(True),
            # state size. (ngf*8) x 4 x 4
            nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ngf * 4),
            nn.ReLU(True),
            # state size. (ngf*4) x 8 x 8
            nn.ConvTranspose2d(ngf * 4, ngf * 2, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ngf * 2),
            nn.ReLU(True),
            # state size. (ngf*2) x 16 x 16
            nn.ConvTranspose2d(ngf * 2, ngf, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ngf),
            nn.ReLU(True),
            # state size. (ngf) x 32 x 32
            nn.ConvTranspose2d(ngf, nc, 4, 2, 1, bias=False),
            nn.Tanh()
            # state size. (nc) x 64 x 64
        )

    def forward(self, input):
        return self.main(input)


# Discriminator Code
class Discriminator(nn.Module):
    def __init__(self, ngpu):
        super(Discriminator, self).__init__()
        self.ngpu = ngpu
        self.main = nn.Sequential(
            # input is (nc) x 64 x 64
            nn.Conv2d(nc, ndf, 4, 2, 1, bias=False),
            nn.LeakyReLU(0.2, inplace=True),
            # state size. (ndf) x 32 x 32
            nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ndf * 2),
            nn.LeakyReLU(0.2, inplace=True),
            # state size. (ndf*2) x 16 x 16
            nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ndf * 4),
            nn.LeakyReLU(0.2, inplace=True),
            # state size. (ndf*4) x 8 x 8
            nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ndf * 8),
            nn.LeakyReLU(0.2, inplace=True),
            # state size. (ndf*8) x 4 x 4
            nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False),
            nn.Sigmoid()
        )

    def forward(self, input):
        return self.main(input)


if __name__ == '__main__':
    # Set random seed for reproducibility
    manualSeed = 999
    # manualSeed = random.randint(1, 10000) # use if you want new results
    print("Random Seed: ", manualSeed)
    random.seed(manualSeed)
    torch.manual_seed(manualSeed)

    # Root directory for dataset
    dataroot = "celeba/"  # 我们加载的数据不需要
    # Number of workers for dataloader
    workers = 0
    # Batch size during training
    batch_size = 128
    # Spatial size of training images. All images will be resized to this
    #   size using a transformer.
    image_size = 64
    # Number of channels in the training images. For color images this is 3 通道数
    nc = 3
    # Size of z latent vector (i.e. size of generator input)
    nz = 100
    # Size of feature maps in generator
    ngf = 64
    # Size of feature maps in discriminator
    ndf = 64
    # Number of training epochs
    num_epochs = 5
    # Learning rate for optimizers
    lr = 0.0002
    # Beta1 hyperparam for Adam optimizers
    beta1 = 0.5
    # Number of GPUs available. Use 0 for CPU mode.
    ngpu = 1

    train_img = np.load('人脸关键点检测挑战赛_数据集/train.npy')
    test_img = np.load('人脸关键点检测挑战赛_数据集/test.npy')
    train_df = pd.read_csv('人脸关键点检测挑战赛_数据集/train.csv')
    train_df = train_df.fillna(48)

    dataloader = torch.utils.data.DataLoader(
        XunFeiDataset(train_img[:, :, :-500], train_df.values[:-500],
                      transform=transforms.Compose([
                          transforms.Resize([64, 64]),
                          transforms.ToTensor(),
                          transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
                      ])
                      ), batch_size=batch_size, shuffle=True, num_workers=workers)
    device = torch.device("cuda:0" if (torch.cuda.is_available() and ngpu > 0) else "cpu")

    # 训练图可视化
    real_batch = next(iter(dataloader))
    plt.figure(figsize=(8, 8))
    plt.axis("off")
    plt.title("Training Images")
    plt.imshow(
        np.transpose(vutils.make_grid(real_batch[0].to(device)[:64], padding=2, normalize=True).cpu(), (1, 2, 0)))
    plt.show()

    # Create the generator
    netG = Generator(ngpu).to(device)

    # Handle multi-gpu if desired
    if (device.type == 'cuda') and (ngpu > 1):
        netG = nn.DataParallel(netG, list(range(ngpu)))

    # Apply the weights_init function to randomly initialize all weights
    #  to mean=0, stdev=0.02.
    netG.apply(weights_init)

    # Print the model
    print(netG)

    # Create the Discriminator
    netD = Discriminator(ngpu).to(device)

    # Handle multi-gpu if desired
    if (device.type == 'cuda') and (ngpu > 1):
        netD = nn.DataParallel(netD, list(range(ngpu)))

    # Apply the weights_init function to randomly initialize all weights
    #  to mean=0, stdev=0.2.
    netD.apply(weights_init)

    # Print the model
    print(netD)

    # Initialize BCELoss function
    criterion = nn.BCELoss()

    # Create batch of latent vectors that we will use to visualize
    #  the progression of the generator
    fixed_noise = torch.randn(64, nz, 1, 1, device=device)

    # Establish convention for real and fake labels during training
    real_label = 1.
    fake_label = 0.

    # Setup Adam optimizers for both G and D
    optimizerD = optim.Adam(netD.parameters(), lr=lr, betas=(beta1, 0.999))
    optimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))

    # Training Loop

    # Lists to keep track of progress
    img_list = []
    G_losses = []
    D_losses = []
    iters = 0

    print("Starting Training Loop...")
    # For each epoch
    for epoch in range(num_epochs):
        # For each batch in the dataloader
        for i, data in enumerate(dataloader, 0):

            ############################
            # (1) Update D network: maximize log(D(x)) + log(1 - D(G(z)))
            ###########################
            ## Train with all-real batch
            netD.zero_grad()
            # Format batch
            real_cpu = data[0].to(device)
            b_size = real_cpu.size(0)
            label = torch.full((b_size,), real_label, dtype=torch.float, device=device)
            # Forward pass real batch through D
            output = netD(real_cpu).view(-1)
            # Calculate loss on all-real batch
            errD_real = criterion(output, label)
            # Calculate gradients for D in backward pass
            errD_real.backward()
            D_x = output.mean().item()

            ## Train with all-fake batch
            # Generate batch of latent vectors
            noise = torch.randn(b_size, nz, 1, 1, device=device)
            # Generate fake image batch with G
            fake = netG(noise)
            label.fill_(fake_label)
            # Classify all fake batch with D
            output = netD(fake.detach()).view(-1)
            # Calculate D's loss on the all-fake batch
            errD_fake = criterion(output, label)
            # Calculate the gradients for this batch, accumulated (summed) with previous gradients
            errD_fake.backward()
            D_G_z1 = output.mean().item()
            # Compute error of D as sum over the fake and the real batches
            errD = errD_real + errD_fake
            # Update D
            optimizerD.step()

            ############################
            # (2) Update G network: maximize log(D(G(z)))
            ###########################
            netG.zero_grad()
            label.fill_(real_label)  # fake labels are real for generator cost
            # Since we just updated D, perform another forward pass of all-fake batch through D
            output = netD(fake).view(-1)
            # Calculate G's loss based on this output
            errG = criterion(output, label)
            # Calculate gradients for G
            errG.backward()
            D_G_z2 = output.mean().item()
            # Update G
            optimizerG.step()

            # Output training stats
            if i % 50 == 0:
                print('[%d/%d][%d/%d]\tLoss_D: %.4f\tLoss_G: %.4f\tD(x): %.4f\tD(G(z)): %.4f / %.4f'
                      % (epoch, num_epochs, i, len(dataloader),
                         errD.item(), errG.item(), D_x, D_G_z1, D_G_z2))

            # Save Losses for plotting later
            G_losses.append(errG.item())
            D_losses.append(errD.item())

            # Check how the generator is doing by saving G's output on fixed_noise
            if (iters % 500 == 0) or ((epoch == num_epochs - 1) and (i == len(dataloader) - 1)):
                with torch.no_grad():
                    fake = netG(fixed_noise).detach().cpu()
                img_list.append(vutils.make_grid(fake, padding=2, normalize=True))

            iters += 1

    plt.figure(figsize=(10, 5))
    plt.title("Generator and Discriminator Loss During Training")
    plt.plot(G_losses, label="G")
    plt.plot(D_losses, label="D")
    plt.xlabel("iterations")
    plt.ylabel("Loss")
    plt.legend()
    plt.show()

    # %%capture
    fig = plt.figure(figsize=(8, 8))
    plt.axis("off")
    ims = [[plt.imshow(np.transpose(i, (1, 2, 0)), animated=True)] for i in img_list]
    ani = animation.ArtistAnimation(fig, ims, interval=1000, repeat_delay=1000, blit=True)

    HTML(ani.to_jshtml())

    # Grab a batch of real images from the dataloader
    real_batch = next(iter(dataloader))

    # Plot the real images
    plt.figure(figsize=(15, 15))
    plt.subplot(1, 2, 1)
    plt.axis("off")
    plt.title("Real Images")
    plt.imshow(
        np.transpose(vutils.make_grid(real_batch[0].to(device)[:64], padding=5, normalize=True).cpu(), (1, 2, 0)))

    # Plot the fake images from the last epoch
    plt.subplot(1, 2, 2)
    plt.axis("off")
    plt.title("Fake Images")
    plt.imshow(np.transpose(img_list[-1], (1, 2, 0)))
    plt.show()

注意:epoch比较小的话,效果比较差,D(G(z))比较小,D(x)占据上风,也就是生成的图片不足以骗过判别器。我们测试epoch=100,生成的图有一些人脸还是比较差。epoch=1000,效果也比较一般,GAN的生成还是比较玄学的。

epoch=100

真实图

loss曲线

生成图

对比图

指标变化

epoch=1000

真实图

loss曲线

生成图

对比图

指标变化

同时也测试了celeb数据集epoch=5的情况,本身数据集比较大,所以epoch=5也花费了不少时间。

真实图

生成图和真实图比较

指标变化

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

推荐阅读更多精彩内容