Pytorch 記錄

1.pytorch中的索引 index_select(x, dim, indices)
dim代表維度蔬螟,indices是篩選的索引序號,一般indices傳入的是torch.LongTensor([1,2])這種類型。見pytorch的切片

2..data是從Variable獲取底層Tensor的主要方式。

3.優(yōu)化器Optimizer都實現(xiàn)了step()方法來對所有的參數(shù)進行更新。
Loss.backward()反向傳播 #This call will compute the gradient of loss with respect to all Tensors with requires_grad=True.
Optimizer.step() 更新參數(shù)

4.torch.squeeze(input, dim=None, out=None)
將某一方向全部相同數(shù)據(jù)壓縮掉绸罗,想想一下,x、y聂儒、z是三維的,如果z全為0硫痰,則可轉化為二維衩婚。

torch.Tensor.view()會將原有數(shù)據(jù)重新分配為一個新的張量,同reshape()
如果有一個維度的大小用-1代替效斑,那么該函數(shù)就會根據(jù)張量總元素的個數(shù)和其他個維度的元素個數(shù)自動計算這個用-1指定的維度的大小非春。

x = torch.randn(2, 4)
#1.5600 -1.6180 -2.0366  2.7115
 #0.8415 -1.0103 -0.4793  1.5734
#[torch.FloatTensor of size 2x4]
y = x.view(4,2)
print y

# 輸出如下
 1.5600 -1.6180
-2.0366  2.7115
 0.8415 -1.0103
-0.4793  1.5734
[torch.FloatTensor of size 4x2]

5.和numpy之間的轉換:from_numpy() numpy()
Pytorch變量類型轉換

6.torch.nn.Embedding
官方文檔說明:


Embedding

參考:word_embeddings_tutorial.以下是筆記以下是筆記以下是筆記(重要的話說三遍)
word embeddings are a representation of the semantics of a word, efficiently encoding semantic information that might be relevant to the task at hand
Central to the idea of deep learning is that the neural network learns representations of the features, rather than requiring the programmer to design them herself. So why not just let the word embeddings be parameters in our model, and then be updated during training? This is exactly what we will do.
Similar to how we defined a unique index for each word when making one-hot vectors, we also need to define an index for each word when using embeddings. These will be keys into a lookup table.
To index into this table, you must use torch.LongTensor (since the indices are integers, not floats).
我們在使用它的時候,需要建立一個語料中所有詞的詞典鳍悠,使用one-hot形式表示所有的的詞税娜。
在實例化embedding類的對象時坐搔,需要傳入的參數(shù)為the vocabulary size, and the dimensionality of the embeddings.(詞典中的單詞個數(shù)藏研,嵌入的詞的維度,padding_idx)概行,其中蠢挡,如果給了padding_idx(int型),那么就是使用padding_idx的值進行嵌入。
使用時凳忙,傳入的input的值應該為 torch.LongTensor业踏,1??要么用torch.tensor()里指定dtype=torch.long2??要么torch.LongTensor([張量])3??要么可以使用data.long()轉換一下.

2019.1.23踩坑:

Traceback (most recent call last):
  File "/Users/yumi/Documents/Code/pytorch_related/classifier/CNN+pooling/code/CNN.py", line 112, in <module>
    pred = model(inputs)
  File "/anaconda3/python.app/Contents/lib/python3.6/site-packages/torch/nn/modules/module.py", line 489, in __call__
    result = self.forward(*input, **kwargs)
  File "/Users/yumi/Documents/Code/pytorch_related/classifier/CNN+pooling/code/CNN.py", line 89, in forward
    out = self.embedded(x)
  File "/anaconda3/python.app/Contents/lib/python3.6/site-packages/torch/nn/modules/module.py", line 489, in __call__
    result = self.forward(*input, **kwargs)
  File "/anaconda3/python.app/Contents/lib/python3.6/site-packages/torch/nn/modules/sparse.py", line 118, in forward
    self.norm_type, self.scale_grad_by_freq, self.sparse)
  File "/anaconda3/python.app/Contents/lib/python3.6/site-packages/torch/nn/functional.py", line 1454, in embedding
    return torch.embedding(weight, input, padding_idx, scale_grad_by_freq, sparse)
RuntimeError: index out of range at /Users/administrator/nightlies/pytorch-1.0.0/wheel_build_dirs/conda_3.6/conda/conda-bld/pytorch_1544137972173/work/aten/src/TH/generic/THTensorEvenMoreMath.cpp:191

出現(xiàn)這個的原因是我發(fā)現(xiàn)傳入的inputs里存在負數(shù),然后debug了一下以前正確的代碼涧卵,我猜想這個地方傳入的必須是非負數(shù)勤家,因為建詞典的時候肯定不存在indices為負數(shù)的情況。
(2019.01.26又出現(xiàn)了這個bug柳恐,原因是我詞匯表的維度設置的太小了)


image.png

p.s.這個地方傳入的inputs的維度是(batch_size,seq_len)

7.torch.nn.LSTM()
官方文檔說明:


LSTM

參考:
LSTM:Pytorch實現(xiàn)
Pytorch的LSTM的理解
聊一聊PyTorch中LSTM的輸出格式
參數(shù):

  • input_size:x的特征維度
  • hidden_size:隱藏層的特征維度
  • num_layers:lstm隱層的層數(shù)伐脖,默認為1
  • bias:False則bih=0和bhh=0. 默認為True
  • batch_first:True則輸入輸出的數(shù)據(jù)格式為 (batch, seq, feature)
  • dropout:除最后一層热幔,每一層的輸出都進行dropout,默認為: 0
  • bidirectional:True則為雙向lstm默認為False

輸入數(shù)據(jù)格式

  • input(seq_len, batch, input_size)
    seq_len體現(xiàn)序列的長度讼庇,也就是這串輸入中明確的單詞的個數(shù)绎巨。第二個維度表示如果希望一次在網(wǎng)絡中走完整個序列可以設置為1,第三個維度是輸入的向量的維度蠕啄,一般是詞嵌入的維度场勤。
  • h0(num_layers * num_directions, batch, hidden_size)
  • c0(num_layers * num_directions, batch, hidden_size)

輸出數(shù)據(jù)格式

  • output(seq_len, batch, hidden_size * num_directions)
    output保存了最后一層,每個time step的輸出h歼跟,如果是雙向LSTM和媳,每個time step的輸出h = [h正向, h逆向] (同一個time step的正向和逆向的h連接起來)。
  • hn(num_layers * num_directions, batch, hidden_size)
    h_n保存了每一層嘹承,最后一個time step的輸出h窗价,如果是雙向LSTM,單獨保存前向和后向的最后一個time step的輸出h叹卷。
  • cn(num_layers * num_directions, batch, hidden_size)
    output保存了最后一層每個time-step的輸出撼港,如果是雙向LSTM,

我們會看到lstm(i.view(1,1,-1),hidden)這種形式骤竹,其實就是輸入的數(shù)據(jù)形式應該為(seq_len, batch, input_size)這種三維的帝牡,hidden的輸入也會有人寫成(h0,c0),然后再去初始化他們的值在這里我們是直接在一開始就將它們初始化并進行了拼接蒙揣。

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

torch.manual_seed(1)
lstm = nn.LSTM(3,3)
# 也就是seq_len是5,5個time-step
inputs = [torch.randn(1,3) for _ in range(5)]
# print(inputs)
# print(torch.randn(1,1,3))

# 也可以在類里自己定義初始化,維度是(num_layers * num_directions, batch, hidden_size)
hidden = (torch.randn(1,1,3),torch.randn(1,1,3))


# 逐個讀入序列的元素
for i in inputs:
    # print(i.view(1,1,-1))
    out,hidden = lstm(i.view(1,1,-1),hidden)
    print("out\n",out)
    print("hidden\n",hidden)

#以下是直接讀入整個序列,LSTM返回的第一個值表示所有時刻的隱狀態(tài)值靶溜,第二個表示最近的隱狀態(tài)值
# 所以下面的out_all和hidden_all是一樣的
#out_all是最后一層每個time-step的輸出值,這里我們只有一層LSTM。
# hidden_all的第一個張量h_n表示的是最后一個time_step的值
inputs = torch.cat(inputs).view(len(inputs),1,-1)
out_all,hidden_all = lstm(inputs,hidden)
print(out_all)
print(hidden_all)

8.產(chǎn)生隨機數(shù)https://zhuanlan.zhihu.com/p/31231210

9.NLLLoss 的 輸入 是一個對數(shù)概率向量和一個目標標簽. 它不會為我們計算對數(shù)概率. 適合網(wǎng)絡的最后一層是log_softmax. 損失函數(shù) nn.CrossEntropyLoss() 與 NLLLoss() 相同, 唯一的不同是它為我們?nèi)プ?softmax.

10.Pytorch數(shù)據(jù)讀取(Dataset, DataLoader, DataLoaderIter)
參考:
Pytorch數(shù)據(jù)讀取
PyTorch源碼解讀之torch.utils.data.DataLoader
Dataset
DataLoader
DataLoaderIter
從上往下裝入 
當代碼運行到要從torch.utils.data.DataLoader類生成的對象中取數(shù)據(jù)的時候,就會調用DataLoader類的iter方法懒震,iter方法就一行代碼:return DataLoaderIter(self)罩息,輸入正是DataLoader類的屬性。因此當調用iter方法的時候就牽扯到另外一個類:DataLoaderIter个扰。

import torch
import torch.utils.data as Data
torch.manual_seed(1)

BATCH_SIZE = 5
x = torch.linspace(1,10,10)
y = torch.linspace(10,1,10)

torch_dataset = Data.TensorDataset(x,y)
loader = Data.DataLoader(
    dataset=torch_dataset,
    batch_size=BATCH_SIZE,
    shuffle=True
)
# 將所有的數(shù)據(jù)訓練了3次
for epoch in range(3):
    #總共有10條數(shù)據(jù),batch_size是5,每個epoch訓練兩次
    for step,(batch_x,batch_y) in enumerate(loader):
        print("Epoch:",epoch,"|step:",step,"|batch x:",batch_x.numpy(),"|batch y:",batch_y.numpy())
print("\n")

結果:
Epoch: 0 |step: 0 |batch x: [ 5.  7. 10.  3.  4.] |batch y: [6. 4. 1. 8. 7.]
Epoch: 0 |step: 1 |batch x: [2. 1. 8. 9. 6.] |batch y: [ 9. 10.  3.  2.  5.]
Epoch: 1 |step: 0 |batch x: [ 4.  6.  7. 10.  8.] |batch y: [7. 5. 4. 1. 3.]
Epoch: 1 |step: 1 |batch x: [5. 3. 2. 1. 9.] |batch y: [ 6.  8.  9. 10.  2.]
Epoch: 2 |step: 0 |batch x: [ 4.  2.  5.  6. 10.] |batch y: [7. 9. 6. 5. 1.]
Epoch: 2 |step: 1 |batch x: [3. 9. 1. 8. 7.] |batch y: [ 8.  2. 10.  3.  4.]

# 如果batch_size是8,不夠分成兩次的瓷炮,那第二次返回的就是剩下的數(shù)據(jù)
# eg.
torch_dataset2 = Data.TensorDataset(x,y)
loader2 = Data.DataLoader(
    dataset=torch_dataset2,
    batch_size= 8 ,
    shuffle=True
)
for epoch in range(3):
    # 總共有10條數(shù)據(jù),batch_size是5,so training for 2 times
    for step, (batch_x, batch_y) in enumerate(loader2):
        print("Epoch:", epoch, "|step:", step, "|batch x:", batch_x.numpy(), "|batch y:", batch_y.numpy())

結果:
Epoch: 0 |step: 0 |batch x: [ 4. 10.  9.  8.  7.  6.  1.  2.] |batch y: [ 7.  1.  2.  3.  4.  5. 10.  9.]
Epoch: 0 |step: 1 |batch x: [5. 3.] |batch y: [6. 8.]
Epoch: 1 |step: 0 |batch x: [9. 8. 4. 6. 5. 3. 7. 2.] |batch y: [2. 3. 7. 5. 6. 8. 4. 9.]
Epoch: 1 |step: 1 |batch x: [10.  1.] |batch y: [ 1. 10.]
Epoch: 2 |step: 0 |batch x: [ 5.  1.  3.  7.  6. 10.  9.  8.] |batch y: [ 6. 10.  8.  4.  5.  1.  2.  3.]
Epoch: 2 |step: 1 |batch x: [2. 4.] |batch y: [9. 7.]

11.Autograd
from torch.autograd import Variable
Tensor在被封裝為Variable后,可以調用.backward實現(xiàn)反向椽筆,自動計算所以梯度...Variable包含三個屬性:data,grad,grad_fn

y = x.detach()正如其名, 將返回一個不參與計算圖的Tensor y, Tensor y 一旦試圖改變修改自己的data, 會被語法檢查和python解釋器監(jiān)測到, 并拋出錯誤.
requires_grad直接掛在Tensor類下,也可以將requires_grad作為一個參數(shù), 構造tensor
https://www.itency.com/topic/show.do?id=494122

# 可學習的參數(shù)可以通過net.parameters()查看
params = list(net.parameters())

# 同時返回可學習的參數(shù)和名稱
for name,parameters in net.named_parameters():
    print(name,":",parameters.size())

Variable節(jié)點和Function節(jié)點,Variable記錄運算數(shù)據(jù)递宅,F(xiàn)unction記錄運算操作娘香。其中Variable節(jié)點又可以分為葉節(jié)點和非葉節(jié)點兩類。葉節(jié)點由用戶直接創(chuàng)建產(chǎn)生办龄,而非葉節(jié)點則由Variable節(jié)點之間的運算操作產(chǎn)生烘绽。
如果一個節(jié)點由用戶創(chuàng)建,則它為葉節(jié)點俐填,對應的grad_fn是None安接。
grad_fn可以查看這個variable的反向傳播函數(shù),grad_fn.next_functions[0][0]

12.pack_padded_sequence VS pad_packed_sequence

torch.nn.utils.rnn.pack_padded_sequence(input, lengths, batch_first=False)
輸入的input需要按長度降序排列英融,lengths需要從大到小排序盏檐。
input的維度:T x B x * 呀打,T表示一個最長的數(shù)據(jù)長度(the length of the longest sequence),B表示batch_size,如果batch_first=True,意味著維度是B x T x *
lengths:每個batch的數(shù)據(jù)長度(Expected len(lengths) to be equal to batch_size)

返回的數(shù)據(jù)是一個PackedSequence 對象糯笙。包含data和batch_size贬丛,其中的data是對input按列算的值,batch_size是這一列去掉padding的0之后的數(shù)量给涕。
參考pytorch中如何處理RNN輸入變長序列padding

排序的思路的話豺憔,可以先對lengths進行sort,然后再使用index_select對input進行排序。

# 降序方法1
sort_index = np.argsort(-np.array(lengths))

# 降序方法2
lengths = torch.tensor(lengths)
_, idx_sort = torch.sort(lengths, dim=0, descending=True)

可以參考pytorch里的pack_padded_sequence和pad_packed_sequence解析
pytorch學習筆記(二十一): 使用 pack_padded_sequence

我的個人方法:

import numpy as np
import torch

data = [[1,1,1,1],[2,2,2,2,2],[3,3,3,3,3,3],[4,4,4,4,4,4,4]]
# print(type(data))
data_length = [len(item) for item in data]
max_len = np.max(data_length)
new_array = np.zeros((len(data), max_len))
for index, data in enumerate(data):
    # print(index,data)
    new_array[index][:len(data)] = data
data_tensor = torch.from_numpy(new_array)
_, indices = torch.sort(torch.tensor(data_length), descending=True)
# print(indices)
print(data_tensor[indices])

_, idx_sort = torch.sort(lengths, dim=0, descending=True)
x = torch.index_select(padding_tensor,0,idx_sort)
x_packed = nn.utils.rnn.pack_padded_sequence(input=x, lengths=lengths[idx_sort],batch_first=True)



把這個PackedSequence 對象送入網(wǎng)絡中够庙,生成出來的還是PackedSequence 對象恭应,這時候就需要pad_packed_sequence啦,pad_packed_sequence是pack_padded_sequence的一個逆操作耘眨。
torch.nn.utils.rnn.pad_packed_sequence(sequence, batch_first=False, padding_value=0.0, total_length=None)
T x B x * 在batch_first=True的情況下返回B x T x *
返回的是一個tuple,第一維是??
第二維是每條數(shù)據(jù)對應的長度昼榛。

13.eval即evaluation模式,train即訓練模式剔难。僅僅當模型中有Dropout和BatchNorm時才會有影響胆屿。因為訓練時dropout和BN都開啟,而一般而言測試時dropout被關閉偶宫,BN中的參數(shù)也是利用訓練時保留的參數(shù)非迹,所以測試時應進入評估模式

  1. nn.Linear(in_features, out_features, bias=True)
    參數(shù):in_features是輸入sample的維度,out_features是輸出sample的維度纯趋。
    輸入:(N, *, in_features): where *? means any number of additional dimensions


    image.png

    image.png
  1. 在進行預測的時候憎兽,需要寫到with torch.no_grad()里面。

16.torch.transpose(input, dim0, dim1)
返回輸入矩陣input的轉置吵冒,交換維度dim0和dim1纯命。輸入張量與輸出張量共享內(nèi)存。

image.png

17.torch.nn.functional.max_pool1d
輸入維度:minibatch??in_channels??iT??iH??iW
根據(jù)torch.nn.MaxPool1d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
輸入的維度是(N,C,Lin)痹栖,輸出是(N,C,Lout)亿汞。
Lout的計算公式:

image.png

18.nn.CrossEntropyLoss()
input的第一項是y_pred,第二項是y_target结耀,今天踩的坑是把它倆位置放反了留夜,百思不得其解報錯信息匙铡。官方文檔是這么說的:


image.png

順便附上這張圖或許可以更好的理解輸出的維度代表的意思:


image.png

19.torch.max(input, dim, keepdim=False, out=None)
input是個tensor
dim為1图甜,表示按行選最大的
dim為0,表示按列選最大的鳖眼。
返回值是(Tensor, LongTensor)黑毅,第一個是max的值,第二個是max的索引值.

怎么用钦讳?可以用來計算預測準確的數(shù)量:

#####二分類的輸出#####
pred = torch.tensor([[0.1,-0.2],[0.3,0.5],[0.2,-0.1]])
label = torch.tensor([0,0,1])
#pred_label為預測的類別矿瘦,label為標注的類別
pred_label = torch.max(pred,1)[1]
print(pred_label)
print(float((pred_label == label).sum())/len(label)*100)

20.2019.01.16今日踩坑:
取tensor中的數(shù)枕面,忘記在哪里看過說如果用tensor.data獲取其中的值并進行疊加的話會加到動態(tài)圖中?缚去?潮秘?
如果一個tensor只有一個元素,那么可以使用tensor.item()方法取出這個元素作為普通的python數(shù)字易结。如果tensor有多個元素枕荞,用tensor.tolist()方法可以取出。

  1. nn.Conv1d & nn.Conv2d
    nn.Conv1d:
    主要參數(shù):input_channel(看一個博客里說這個是詞嵌入的維度), output_channel, kernel_size, stride, padding.
    輸入:(batch_size, num of input channels搞动,文本長度)
    輸出:(batch_size, num of output channels(我理解的是用多少個卷積核去卷積),Lout(這里是用那個卷積的公式計算出來的))
# 16表示input_channel,是詞嵌入的維度躏精,33是out_channel,是用幾個卷積核進行卷積鹦肿,3是表示卷積核的大小矗烛,這里是(3*詞嵌入的維度)
m = nn.Conv1d(16, 33, 3, stride=2)
# input2 = torch.randn()
# 輸入:N*C*L:batch_size為20,C為詞嵌入的維度箩溃,50為句子的length
# 輸出:N*Cout*Lout:Cout我理解的是out_channel的數(shù)量
#要注意輸入的維度是詞嵌入在第二維瞭吃,還是句子的length在第二維,不行就用permute()或者transpose()修改維度的順序涣旨。
input2 = torch.randn(20, 16, 50)
output2 = m(input2)
print(output2.size())

22.2019.01.25今日踩坑:
contiguous()一般與transpose虱而,permute,view搭配使用
即使用transpose或permute進行維度變換后,調用contiguous开泽,然后方可使用view對維度進行變形牡拇。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市穆律,隨后出現(xiàn)的幾起案子惠呼,更是在濱河造成了極大的恐慌,老刑警劉巖峦耘,帶你破解...
    沈念sama閱讀 212,294評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剔蹋,死亡現(xiàn)場離奇詭異,居然都是意外死亡辅髓,警方通過查閱死者的電腦和手機泣崩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,493評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洛口,“玉大人矫付,你說我怎么就攤上這事〉谘妫” “怎么了买优?”我有些...
    開封第一講書人閱讀 157,790評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我杀赢,道長烘跺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,595評論 1 284
  • 正文 為了忘掉前任脂崔,我火速辦了婚禮滤淳,結果婚禮上,老公的妹妹穿的比我還像新娘砌左。我一直安慰自己娇钱,他們只是感情好,可當我...
    茶點故事閱讀 65,718評論 6 386
  • 文/花漫 我一把揭開白布绊困。 她就那樣靜靜地躺著文搂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪秤朗。 梳的紋絲不亂的頭發(fā)上煤蹭,一...
    開封第一講書人閱讀 49,906評論 1 290
  • 那天,我揣著相機與錄音取视,去河邊找鬼硝皂。 笑死,一個胖子當著我的面吹牛作谭,可吹牛的內(nèi)容都是我干的稽物。 我是一名探鬼主播,決...
    沈念sama閱讀 39,053評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼折欠,長吁一口氣:“原來是場噩夢啊……” “哼贝或!你這毒婦竟也來了?” 一聲冷哼從身側響起锐秦,我...
    開封第一講書人閱讀 37,797評論 0 268
  • 序言:老撾萬榮一對情侶失蹤咪奖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后酱床,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羊赵,經(jīng)...
    沈念sama閱讀 44,250評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,570評論 2 327
  • 正文 我和宋清朗相戀三年扇谣,在試婚紗的時候發(fā)現(xiàn)自己被綠了昧捷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,711評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡罐寨,死狀恐怖靡挥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情衩茸,我是刑警寧澤芹血,帶...
    沈念sama閱讀 34,388評論 4 332
  • 正文 年R本政府宣布,位于F島的核電站楞慈,受9級特大地震影響幔烛,放射性物質發(fā)生泄漏。R本人自食惡果不足惜囊蓝,卻給世界環(huán)境...
    茶點故事閱讀 40,018評論 3 316
  • 文/蒙蒙 一饿悬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧聚霜,春花似錦狡恬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,796評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至姥芥,卻和暖如春兔乞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背凉唐。 一陣腳步聲響...
    開封第一講書人閱讀 32,023評論 1 266
  • 我被黑心中介騙來泰國打工庸追, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人台囱。 一個月前我還...
    沈念sama閱讀 46,461評論 2 360
  • 正文 我出身青樓淡溯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親簿训。 傳聞我的和親對象是個殘疾皇子咱娶,可洞房花燭夜當晚...
    茶點故事閱讀 43,595評論 2 350

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