恒源云(GPUSHARE)_未聞Prompt名(論文學習筆記)

文章來源 | 恒源云社區

原文地址 |未聞Prompt名

原文作者 | Mathor


前言:

個人覺得2021年NLP最火的兩個idea,一個是對比學習(Contrastive Learning),另一個就是Prompt

淺談我對 Prompt 的理解

Prompt 說簡單也簡單,看了幾篇論文以及博客后發現其實就是構建一個語言模版。但是細想起來又覺得復雜,因為總感覺里面還有很多細節,因此本文就來從頭梳理一下 Prompt(Prompt 很多地方會翻譯成「范式」,但是「范式」這個詞本身也不好理解,因此讀者把他看作是「模板」即可)

今天我還與室友討論預訓練模型(例如 BERT)到底做了什么,我給出的回答是

預訓練模型提供了一個非常好的初始化參數,這組參數在預訓練任務上的表現非常好(預訓練損失非常低),但是由于下游任務千奇百怪,我們需要在這組參數的基礎上進行 Fine-tune 以適應我們的下游任務(使得下游任務的損失值非常低)

上面這段話其實隱含了目前做 NLP 任務的大致流程,即 "Pre-train, Fine-tune",而對我們來說實際上大部分時候都是直接拿別人預訓練好的模型做 Fine-tune,并沒有 Pre-train 這一步

融入了 Prompt 的模式大致可以歸納成 "Pre-train, Prompt, and Predict",在該模式中,下游任務被重新調整成類似預訓練任務的形式。例如,通常的預訓練任務有 MLM(Masked Language Model),在文本情感分類任務中,對于 "I love this movie" 這句輸入,可以在后面加上 Prompt:"the movie is ___",組成如下這樣一句話:

I love this movie, the movie is ___

然后讓預訓練模型用表示情感的答案(例如 "great"、"terrible" 等)做完形填空,最后再將該答案轉換為情感分類的標簽。這樣一來,我們就可以通過構造合適的「模板」,通過小樣本數據集訓練一個模型來解決各種各樣的下游任務

注意,Prompt 設計的這種完形填空和 MLM 任務是有區別的,二者雖然都是都是詞分類,但是候選集不同,MLM 的候選詞是整個詞庫,不過如果是生成任務,那么 Prompt 和 MLM 的候選集就是一樣的,都是整個詞庫

如何構建 Prompt

對于輸入文本 x,存在一個函數 fPrompt(x),將 x 轉化成 x′ 的形式,即
x^{’}=f_{\text{Prompt}}(x)
該函數通常會進行兩步操作:

  1. 使用一個模板,模板通常為一段自然語言句子,并且該句子包含兩個空位置:用于填輸入 x 的位置 [X]、用于生成答案文本 z 的位置 [Z]
  2. 把輸入 x 填到 [X] 的位置

以前文提到的例子為例,在文本情感分類任務中,假設輸入是

x = "I love this movie"

使用的模板是

[X]. Overall, it was a [Z] movie

那么得到的 x′ 就應該是

I love this movie. Overall, it was a [Z] movie

在實際情況中,Prompt 來填充答案的位置一般在句中或句末。如果在句中,一般稱這種 Prompt 為 Cloze Prompt;如果在句末,一般稱這種 Prompt 為 Prefix Prompt[X][Z] 的位置、數量以及使用模板句的不同,都有可能對結果造成影響,因此需要靈活調整

上面講的都是簡單的情感分類任務的 Prompt 設計,讀者看到這里自然而然的會想到,其他 NLP 任務的 Prompt 如何設計呢?實際上劉鵬飛大神在他的論文中給我們提供了一些參考

Text Generation 中摘要任務里有一個關鍵字 TL;DR,這其實是 Too Long; Don't Read 的縮寫

Prompt 的選擇非常重要且困難

有上述 Prompt 的基礎后,我們可以得知 Prompt 的設計主要包含兩部分:

  1. 模板 T:例如 [X]. Overall, It was [Z]
  2. 標簽詞映射:即 [Z] 位置預測輸出的詞匯集合與真實標簽 y 構成的映射關系。例如,標簽 positive 對應單詞 great,標簽 negative 對應單詞 terrible

在基于 Prompt 的微調方法中,不同的模板和標簽詞對最終結果影響很大,下圖是陳丹琦團隊論文中的實驗結果

從上圖我們可以看出兩點:

  1. 使用相同的「模板」,不同的「標簽詞」會產生不一樣的效果。例如 great/terribelcat/dog 這兩組標簽詞的效果不一樣,而且即便是相同標簽詞,互換順序也會導致最終效果有所變化,例如 cat/dogdot/cat
  2. 使用相同「標簽詞」,對「模板」進行小改動(例如增刪標點)也會呈現不同的結果

Prompt 的設計

Prompt 大概可以從下面三個角度進行設計:

  • Prompt 的形狀
  • 人工設計模板
  • 自動學習模板

Prompt 的形狀

Prompt 的形狀主要指的是 [X][Z] 的位置和數量。上文提到的 Cloze Prompt 與 Maksed Language Model 的訓練方式非常類似,因此對于 MLM 任務來說,Cloze Prompt 更合適;對于生成任務或者使用自回歸 LM 解決的任務,Prefix Prompt 更合適

人工設計模板

Prompt 的模板最開始是人工設計的,人工設計一般基于人類的自然語言知識,力求得到語義流暢且高效的「模板」。例如,Petroni 等人在著名的 LAMA 數據集中為知識探針任務人工設計了 Cloze Templates;Brown 等人為問答、翻譯和探針等任務設計了 Prefix Templates。人工設計模板的優點是直觀,但缺點是需要很多實驗、經驗以及語言專業知識。下圖是 GPT Understands, Too 論文中的一個實驗結果

可以看到不同的 Prompt 只有細微的區別,有的甚至只是增加減少一個詞,但是最后的結果會差幾十個點

自動學習模板

為了解決人工設計模板的缺點,許多研究員開始探究如何自動學習到合適的模板。自動學習的模板又可以分為離散(Discrete Prompts)和連續(Continuous Prompts)兩大類。離散方法主要包括:Prompt Mining,Prompt Paraphrasing,Gradient-based SearchPrompt GenerationPrompt Scoring;連續的則主要包括 Prefix TuningTuning Initialized with Discrete promptsHard-Soft Prompt Hybrid TuningP-Tuning v2

離散 Prompts

簡單說一下上述幾種方法,首先是離散的 Prompt Mining,這篇文章發表在 TACL 2020,講的是如何拿預訓練語言模型當作「知識庫」使用,并且引入了依存樹和 Paraphrase(轉述)等方法來挖掘更好的「模板」,下圖是實驗結果

可以看到,被挖掘出來的若干「連接謂詞」相比于人工設計的「模板」結果提升還是很明顯的

有很多種方法可以實現 Prompt Paraphrsing,例如「回譯」,我們通過 DeepL 翻譯看個例子:


這樣我們就得到了 x shares a border with y 的一個 Prompt Paraphrasing:x and y share a boundary

論文 BARTScore 干脆給我們提供了一張表,里面有各種詞組的同義替換,這個我再熟悉不過了,因為以前英語考試我也背過類似的東西

Gradient-based Search(基于梯度的搜索)是由論文 AUTOPROMPT 提出的,這篇文章發表在 EMNLP 2020,它的主要思想用下面這張圖就可以表示

上圖中,a real joy 是原始的輸入句子 xinp,紅色的 Trigger tokens 是由 xinp「激發」的相關詞匯集合 xtrig,根據 Template λ 的配置,將 xtrig 和 xinp 組合起來構造最終的輸入 xprompt,送入 Masked LM 預測情感標簽。下面的表格增加了很多 NLP 其他任務的例子

關于如何生成 xtrig 集合,實際上主要使用的是 HotFlip 和對抗訓練的思想,感興趣的同學可以看原論文以及 HotFlip: White-box adversarial examples for text classificationUniversal Adversarial Triggers for Attacking and Analyzing NLP 這兩篇論文

Prompt Generation 是陳丹琦團隊的一項工作,主要是把 Seq2Seq 預訓練模型 T5 應用到模板搜索的過程。T5 基于多種無監督目標進行預訓練,其中最有效的一個無監督目標就是:利用 <X>< Y > 替換一個或多個連續 span,然后生成對應輸出。例如:

Thank you <X> me to your party <Y> week

T5 會在 <X> 生成 for inviting,在 <Y> 生成 last。很顯然,T5 這種方式很適合生成模板,而且不需要指定模板的 token 數。具體來說,有三種可能的生成方式

<S_1> <span>→</span> <{X}> {M}(y) <{Y}> <S_1>

<S_1> <span>→</span> <S_1><{X}>{M}(y) <{Y}>

<S_1> <S_2><span>→</span> <S_1> <{X}> {M}(y) <{Y}> <S_2>

具體的模板生成過程如下圖所示:

首先在標簽詞前后添加填充位 <X>< Y>(上面提到的三種生成方式),然后將其送入 T5 模型中,T5 會自動在填充位生成序列,最后將標簽詞(great 或 terribel)轉換為 [MASK] 標簽,形成多個模板。具體過程中采用 Beam Search 的方法生成多個候選模板,然后對每一個候選模板利用 dev 集進行微調,選擇其中一個最佳模板

我還想說一下這篇論文中另外一個有意思的點,最后送入模型進行預測的句子還拼接上了每種類別的「示例」(Demonstration),如下圖所示

這種 Prompt 的設計有點像是在做語義相似度任務,X 為原始 Input 句子,已知 Y 為正例,Z 為負例,構造了如下形式的輸入:

X是[MASK]例?Y為正例;Z為負例

這有點像是編程語言中的三目運算符,或者說相當于讓模型比較 XYZ 的語義相似度。這里我們自然而然會想問:YZ 是如何挑選出來的?實際上是依據下面兩條規則:

  1. 對于每個原始輸入句子,從每個類別中隨機采樣一個樣本「示例」拼接到 Prompt 中
  2. 對于每個原始輸入句子,在每個類別中,通過與 Sentence-BERT 進行相似度計算,從相似度最高的前 50% 樣本中隨機選擇一個樣本「示例」

連續 Prompts

構造 Prompt 的初衷是能夠找到一個合適的方法,讓 Pre-trained Language Model(PLM)更好地輸出我們想要的結果,但其實并不一定要將 Prompt 的形式設計成人類可以理解的自然語言,只要機器理解就行了。因此,還有一些方法探索連續型 Prompts—— 直接作用到模型的 Embedding 空間。連續型 Prompts 去掉了兩個約束條件:

  1. 模版中詞語的 Embedding 可以是整個自然語言的 Embedding,不再只是有限的一些 Embedding
  2. 模版的參數不再直接取 PLM 的參數,而是有自己獨立的參數,可以通過下游任務的訓練數據進行調整

Prefix Tuning 最開始由 Li 等人提出,這是一種在輸入句子前添加一組連續型向量的方法,該方法保持 PLM 的參數不動,僅訓練前綴(Prefix)向量。Prefix Tuning 的提出主要是為了做生成任務,因此它根據不同的模型結構定義了不同的 Prompt 拼接方式,在 GPT 類的 Auto-Regressive(自回歸)模型上采用的是 [Prefix;x;y] 的方式,在 T5 類的 Encoder-Decoder 模型上采用的是 [Prefix;x;Prefix;y] 的方式

輸入部分 Prefix,x,y 的 Position id 分別記作 Pidx,Xidx,Yidx。Prefix Tuning 初始化一個可訓練的矩陣,記作 Pθ∈R|Pidx|×dim?(hi),其中

上述公式的含義是,索引 i 如果屬于前綴的部分,則從 Pθ 中抽取向量;i 如果不是前綴部分,則由參數固定的預訓練模型生成對應的向量。訓練目標為:
\mathop{\text{max}}\limits_{\phi} \ \log p_{\phi}(y\mid x) = \sum\limits_{i\in \text{Y}{\text{idx}}} \log p{\phi} (z_i\mid h_{<i})

Pθ本質上是一個矩陣,而生成一個矩陣的方法又很多,可以用 nn.Embedding(),或者 nn.Linear()

同樣是在連續空間上搜索 Prompt,OptiPrompt 構建的「模板」并不局限于前綴,也可以在句子的中間

首先根據 AutoPrompt 定義一個 Prompt 模板:
[x]\ [v]_1\ [v]_2\ …\ [v]_m\ [\text{MASK}]
其中 [v]i 為一個連續型向量(與 BERT 的輸入維度一致)。OptiPrompt 還考慮以人工構建的離散 Prompt 作為起點,在連續空間上進行搜索以構建較優的 Prompt。例如 [x] is [MASK] citizen 可以轉換為
[x]\ [v]_1\ [\text{MASK}]\ [v]_2
iscitizen 對應的 input Embedding 作為 [v]1[v]2 的初始化

Hard-Soft Prompt Hybrid Tuning 方法可以說是人工設計和自動學習的結合,它通常不單純使用可學習的 Prompt 模板,而是在人工設計的模板中插入一些可學習的 Embedding。實際上有了上面的基礎我們都知道,連續的 Prompt 要比離散的 Prompt 好一點,但是在此基礎上還有什么改進的余地嗎?Liu 等人提出的 P-Tuning 解決了 Prompt token 之間的關聯性問題

之前連續的 Prompt 生成方式無非都是訓練一個矩陣,然后通過索引出矩陣的某幾行向量拼起來。坦白地說,我們希望這些 prompt token Embedding 之間有一個比較好的關聯性,而不是獨立地學習,為了解決這個問題,P-Tuning 引入了一個 Prompt Encoder(如下圖 b 所示)

上圖 a 是傳統的離散型 Prompt,我們把生成離散 Prompt token 的東西叫做 Prompt Generator;上圖 b 首先傳入一些 Virtual(Pseudo)token,例如 BERT 詞表中的 [unused1],[unused2],... 當然,這里的 token 數目是一個超參數,插入的位置也可以調整。將這些 Pseudo token 通過一個 Prompt Encoder 得到連續的向量 h0,...,hm,其中

即,Prompt Encoder 是由 BiLSTM+MLP 組成的一個簡單網絡。作者還發現加入一些 anchor token(領域或者任務相關的 token)可以有助于 Template 的優化。例如文本蘊含任務,輸入是前提和假設,判斷是否蘊含。一個連續的模版是
\text{[PRE]} [\text{continuous tokens}][\text{HYP}][\text{continuous tokens}] [\text{MASK}]
在其中加入一個anchor token:[?]效果會更好,此時模板變成
\text{[PRE]} [\text{continuous tokens}][\text{HYP}]?[\text{continuous tokens}] [\text{MASK}]

大家可能想問,如何優化 P-tuning?實際上根據標注數據量的多少,分兩種情況討論

  1. 標注數據比較少。這種情況,我們固定 PLM 的參數,只優化 [P0]~[Pm] 這幾個 token 的 Embedding。換句話說,我們只是要更新 Prompt Encoder 的參數
  2. 標注數據很充足。這種情況直接放開所有參數微調

就在 P-Tuning 方法提出不久后,Liu 等人又提出了 P-Tuning v2,主要解決 P-Tuning 的兩個問題:

  1. 當預訓練模型的參數量低于 100 億(10B)時,Prompt tuning 會比傳統的 Fine-tuning 差
  2. 諸如序列標注這樣對推理和理解要求高的任務,prompt tuning 效果會變差

Liu 等人認為先前的 P-Tuning 只用了一層 BiLSTM 來編碼 Pseudo token,這是其推理能力不足的原因之一,因此 v2 版本提出 Deep Prompt Tuning,用 Prefix Tuning 中的深層模型替換 BiLSTM,如下圖所示

P-Tuning v2 相比于 P-Tuning,區別在于:

  • 取消 Reparameterization:以前的方法利用重參數化功能來提高訓練速度和魯棒性(例如,用于 Prefix-Tuning 的 MLP 和用于 P-Tuning 的 LSTM)。在 P-Tuning v2 中,作者發現重參數化的改進很小,尤其是對于較小的模型,同時還會影響模型的表現
  • Multi-task Learning:Deep Prompt Tuning 的優化難題可以通過增加額外的任務數據或者無標注數據來緩解,同時可微調的 Prefix Continuous Prompt 也可以用來做跨任務的知識共享。例如在 NER 中,可以同時訓練多個數據集,不同數據集使用不同的頂層 Classifier,但是 Prefix Continuous Prompt 是共享的
  • 取消 verbalizer:v2 取消了標簽映射,完全變為生成模型,可以在 [CLS] 部分輸出句子級別的標簽(Sentence-level label),也可以在每個 token 位置輸出 token 級別的標簽(Token-level label),直接輸出真實標簽

關于 P-Tuning 還有一些碎碎念,主要是從各個博客上看到的,匯總在這里。首先是 v1 版本的 LSTM,實際上引入 LSTM 目的是為了幫助「模板」生成的 token(某種程度上)更貼近自然語言,或者說 token 之間的語義更流暢,但更自然的方法應該是在訓練下游任務的時候,不僅預測下游任務的目標 token(例如 "great"、"terrible"),還應該同時做其他 token 的預測

比如,如果是 MLM 模型,那么也隨機 MASK 掉其它的一些 token 來預測,如果是 LM 模型,則預測完整的序列,而不單單是目標詞。這樣做的理由是:因為我們的 MLM/LM 都是經過自然語言預訓練的,所以我們認為它能夠很好的完成序列的重構,即便一開始不能,隨著迭代輪數的增加,模型也能很好完成這項任務。所以這本質上是讓模型進行「負重訓練」

* 為什么要引入 Prompt?

在標準的 Fine-tune 過程中(如上圖 b 所示),新引入的參數量可能會很大(獨立于原始預訓練模型外的參數),例如基于 RoBERTa-large 的二分類任務會新引入 2048 個參數(nn.Linear(1024, 2)),如果你僅有例如 64 個標注數據這樣的小樣本數據集,微調會非常困難

為解決這一問題,Prompt 應運而生(如上圖 a 所示),直接將下游任務轉換為輸出空間有限的 MLM 任務。值得注意的是:上述方法在預訓練參數的基礎上進行微調,并且沒有引入任何新參數,同時還減少了微調和預訓練任務之間的差距。總的來說,這可以更有效地用于小樣本場景

Prompt 的挑戰與展望

盡管 Prompt 研究搞得如火如荼,但目前仍存在許多問題值得研究者們去探究

  1. Prompt 的設計問題。目前使用 Prompt 的工作大多集中于分類任務和生成任務,其它任務則較少。另外,「模板」和「答案」的聯系也亟待解決。模型的表現同時依賴于使用的「模板」和「答案」的映射,如何同時搜索或者學習出兩者聯合的最好效果仍然很具挑戰性
  2. Prompt 的理論分析和可解釋性。盡管 Prompt 方法在很多情況下都取得了成功,但是目前 Prompt-based Learning 理論分析還很少,人們很難了解 Prompt 為什么能達到好的效果,又為什么在自然語言中意義相近的 Prompt 有時效果卻相差很大
  3. Prompt 在 PLM debias 方面的應用。由于 PLM 在預訓練過程中見過了大量的人類世界的自然語言,所以很自然地會受到一些影響。舉一個簡單的例子,比如說訓練語料中有非常多 "The capital of China is Beijing",導致模型每次看到 "capital" 的時候都會預測出 "Beijing",而不是去分析到底是哪個國家的首都。在應用的過程中,Prompt 還暴露了 PLM 學習到的很多其它 bias,比如種族歧視、性別對立等。這也許會是一個值得研究的方向

One More Thing

最后我還想提一個實際 Code 過程中存在的問題。我們知道 MLM 任務會輸出句子中 [MASK] 位置最有可能的詞,而 Prompt 也類似的,例如下面的例子

這是一條__新聞。中國足球出線的可能性只有0.001%,留給中國隊的時間不多了

這是一個新聞分類問題,真實標簽有 "體育"、"財經"、"娛樂" 等,上面的樣本很明顯是一條體育新聞,因此我們希望模型對 [MASK] 部分輸出 "體育",但事實真的如此嗎?實際情況模型的輸出可能是 "足球",但你認為模型預測的 "足球" 有問題嗎?好像也沒啥毛病,因此這就引申出了 Prompt 的一個問題,是否應該限制模型的輸出空間?

還是上面新聞分類的例子,我們是否應該限制模型輸出的空間,讓他固定只能預測 "體育"、"財經"、"娛樂" 這幾個標簽?或者我們干脆把這幾個標簽換成索引,那就是讓模型從 0,1,2 這三個數字選一個。Wait Wait Wait,如果這么做的話,和 Fine-Tune 有什么區別,Fine-Tune 也是把標簽轉換成索引,讓模型看了句子之后,從這幾個索引中選一個作為預測值

這么說的話,那我們就不應該限制模型的輸出空間,可是這樣的話 [MASK] 位置的輸出就限制的太死了,必須一定是 "good"、"財經" 才算對,如果輸出 "nice"、"財政" 就算錯。實際上輸出近義詞或者相似詞,在零樣本的情況下會經常出現,但是如果你用一些有標簽的樣本去訓練,模型自己就會慢慢固定輸出空間。例如 "財經",它不會預測成 "財政",只會預測成其它類型的新聞,例如 "體育"

References

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容