推薦系統(tǒng)(四):word2vec在推薦系統(tǒng)的應用

一烛恤、Word2Vec算法

Word2Vec簡單講就是通過學習文本然后用詞向量的方式表征詞的語義信息拧烦,即通過Embedding把原先詞所在空間映射到一個新的空間中去,使得語義上相似的單詞在該空間內距離相近莉擒。以傳統(tǒng)神經網絡為基礎的神經概率語言模型酿炸,缺點主要是計算量太大,集中體現(xiàn)在:隱層和輸出層之間的矩陣運算和輸出層上的 Softmax歸一化運算上涨冀。因此Word2Vec就是針對這兩點來優(yōu)化神經概率語言模型的填硕。 中兩個重要的模型是: CBOW模型和 Skip-gram模型。

1. CBOW模型

CBOW(continue bag-of-words) 包括三層結構:輸入層鹿鳖、投影層和輸出層扁眯。


輸入層:包含2c個詞的詞向量。
投影層:對輸入的2c個詞向量進行累加操作翅帜。
輸出層:輸出層對應一顆哈夫曼(Huffman)樹姻檀,它是以語料中出現(xiàn)過的詞當葉子節(jié)點,以各詞在語料庫中出現(xiàn)的次數(shù)當權值構造而成涝滴。在這顆樹中绣版,葉子結點共N個,分別對應詞典\mathbb{D}中的詞歼疮,非葉結點N-1個杂抽。

Hierarchical Softmax 的思想,對于詞典 \mathbb{D} 中的的任意詞 w 韩脏,哈夫曼樹中必然存在唯一一條從根節(jié)點到詞 w 對應葉子節(jié)點的路徑 p^w缩麸。路徑 p^w中包含節(jié)點的個數(shù)為l^w,則路徑 p^w 上存在 l^w-1 個分支赡矢,將每個分支看作一次二分類杭朱,那么每一次分類就對應一個概率阅仔,最后這些概率連乘得到p(w | \text { Context }(w)),即
p(w | \text { Context }(w)) =\prod_{j=2}^{l^{w}} p\left(d_{j}^{w} | X_{w} ; \theta_{j-1}^{w}\right)

其中d_{j}^{w}表示路徑p^w中第j個節(jié)點對應的編碼(根節(jié)點不對應編碼)弧械;\theta_{j}^{w}表示路徑p^w中第j個非葉子節(jié)點對應的向量八酒;X_{w}表示 \text { Context }(w)中所有詞向量的疊加。

\begin{aligned} p\left(d_{j}^{w} | X_{w} ; \theta_{j-1}^{w}\right) &=\left\{\begin{array}{ll}{\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right),} & {\text { if } d_{j}^{w}=0} \\ {1-\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right),} & {\text { otherwise }} \end{array}\\ {} {=\left[\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right]^{1-d_{j}^{w}} \cdot\left[1-\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right]^{d_{j}^{w}}}\right.\end{aligned}

其中\sigma(x) = \frac{1}{1+e^{-x}}梦谜。通過對數(shù)極大似然化處理可得CBOW模型的目標函數(shù)為:
\begin{aligned} \mathcal{L} &=\sum_{w \in \mathcal{D}} \log \prod_{j=2}^{l^{w}}\left(\left[\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right]^{1-d_{j}^{w}} \cdot\left[1-\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right]^{d_{j}^{w}}\right) \\ &=\sum_{w \in \mathcal{D}} \sum_{j=2}^{l^{w}}\left(\left(1-d_{j}^{w}\right) \cdot \log \left[\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right]+d_{j}^{w} \cdot \log \left[1-\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right]\right) \\ &=\sum_{w \in \mathcal{D}} \sum_{j=2}^{l^{w}} \Phi\left(\theta_{j-1}^{w}, X_{w}\right) \end{aligned}

word2vec極大化目標函數(shù)使用的算法是隨機梯度上升法丘跌,首先考慮\Phi\left(\theta_{j-1}^{w}, X_{w}\right)\theta_{j-1}^{w}的梯度計算:
\begin{aligned} \frac{\partial \Phi\left(\theta_{j-1}^{w}, X_{w}\right)}{\partial \theta_{j-1}^{w}} &=\frac{\partial}{\theta_{j-1}^{w}}\left(\left(1-d_{j}^{w}\right) \cdot \log \left[\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right]+d_{j}^{w} \cdot \log \left[1-\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right]\right) \\ &=\left(1-d_{j}^{w}\right)\left[1-\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right] X_{w}-d_{j}^{w} \sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right) X_{w} \\ &=\left(\left(1-d_{j}^{w}\right)\left[1-\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right]-d_{j}^{w} \sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right) X_{w} \\ &=\left(1-d_{j}^{w}-\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right) X_{w} \end{aligned}

\theta_{j-1}^{w}的更新公式為:
\theta_{j-1}^{w}:=\theta_{j-1}^{w}+\eta\left(1-d_{j}^{w}-\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right) X_{w}

然后考慮\Phi\left(\theta_{j-1}^{w}, X_{w}\right)X_{w}的梯度計算:
\begin{aligned} \frac{\partial \Phi\left(\theta_{j-1}^{w}, X_{w}\right)}{\partial X_{w}} &=\frac{\partial}{X_{w}}\left(\left(1-d_{j}^{w}\right) \cdot \log \left[\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right]+d_{j}^{w} \cdot \log \left[1-\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right]\right) \\ &=\left(1-d_{j}^{w}\right)\left[1-\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right] \theta_{j-1}^{w}-d_{j}^{w} \sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right) \theta_{j-1}^{w} \\ &=\left(\left(1-d_{j}^{w}\right)\left[1-\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right]-d_{j}^{w} \sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right) \theta_{j-1}^{w} \\ &=\left(1-d_{j}^{w}-\sigma\left(X_{w}^{T} \theta_{j-1}^{w}\right)\right) \theta_{j-1}^{w} \end{aligned}

觀察到\Phi\left(\theta_{j-1}^{w}, X_{w}\right)\theta_{j-1}^{w}X_{w}具有對稱性,word2vec直接取下式方式來更新v(\widetilde{w})
v(\widetilde{w}):=v(\widetilde{w})+\eta \sum_{j=2}^{l^{w}} \frac{\partial \Phi\left(\theta_{j-1}^{w}, X_{w}\right)}{\partial X_{w}}, \widetilde{w} \in \text { Context }(w)

2. Skip-gram模型

Skip-gram模型也包括輸入層唁桩、投
影層和輸出層闭树。


輸入層:只含有當前樣本的中心詞w的詞向量v(w) \in \mathbb{R}
投影層:由于為恒等投影荒澡,因此該層可有可無报辱;
輸出層:也為一棵哈夫曼樹。

Skip-gram模型中已知當前詞w单山,需要對其上下文Context(w)中的詞進行預測碍现,關鍵是條件概率函數(shù)p(\text {Context}(w) | w),即:

p(\text {Context}(w) | w)=\prod_{u \in C \text {ontext}(w)} p(u | w)

同樣由Hierarchical Softmax的思想米奸,可得:
\begin{aligned} p(u | w) &=\prod_{j=2}^{l^{w}} p\left(d_{j}^{u} | v(w) ; \theta_{j-1}^{u}\right) \\ &=\prod_{j=2}^{l^{u}}\left[\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right]^{1-d_{j}^{u}} \cdot\left[1-\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right]^{d_{j}^{u}}\right. \end{aligned}

通過極大似然化處理可得Skip-gram模型的目標函數(shù)為:
\begin{aligned} {L} &=\sum_{w \in {D}} \log \prod_{u \in C o n t e x t} \prod_{j=2}^{l^{u}}\left(\left[\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right]^{1-d_{j}^{u}} \cdot\left[1-\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right]^{d_{j}^{u}}\right)\right.\\ &=\sum_{w \in {D}} \sum_{u \in C o n t e x t} \sum_{j=2}^{l^{u}}\left(\left(1-d_{j}^{u}\right) \cdot \log \left[\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right]+d_{j}^{u} \cdot \log \left[1-\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right]\right) \\ &=\sum_{w \in {D}} \sum_{u \in \text {Context}(w)} \sum_{j=2}^{l^{u}} {O}\left(\theta_{j-1}^{u}, v(w)\right) \end{aligned}

考慮{O}\left(\theta_{j-1}^{u}, v(w)\right)\theta_{j-1}^{u}的梯度:
\begin{aligned} \frac{\partial {O}\left(\theta_{j-1}^{u}, v(w)\right)}{\partial \theta_{j-1}^{u}} &\left.=\frac{\partial}{\partial \theta_{j-1}^{u}}\left(\left(1-d_{j}^{u}\right) \cdot \log \left[\sigma(w)^{T} \theta_{j-1}^{u}\right)\right]+d_{j}^{u} \cdot \log \left[1-\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right]\right) \\ &=\left(1-d_{j-1}^{u}\right)\left[1-\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right] v(w)-d_{j}^{u} \sigma\left(v(w)^{T} \theta_{j-1}^{u}\right) v(w) \\ &=\left(\left(1-d_{j}^{u}\right)\left[1-\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right]-d_{j}^{u} \sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right) v(w) \\ &=\left(1-d_{j}^{u}-\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right) v(w) \end{aligned}

\theta_{j-1}^{u}的更新公式為:
\theta_{j-1}^{u}:=\theta_{j-1}^{u}+\eta\left(1-d_{j}^{u}-\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right) v(w)

再考慮{O}\left(\theta_{j-1}^{u}, v(w)\right)v(w)的梯度:
\begin{aligned} \frac{\partial {O}\left(\theta_{j-1}^{u}, v(w)\right)}{\partial v(w)} &=\frac{\partial}{\partial v(w)}\left(\left(1-d_{j}^{u}\right) \cdot \log \left[\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right]+d_{j}^{u} \cdot \log \left[1-\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right]\right) \\ &=\left(1-d_{j}^{u}\right)\left[1-\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right] \theta_{j-1}^{u}-d_{j}^{u} \sigma\left(v(w)^{T} \theta_{j-1}^{u}\right) \theta_{j-1}^{u} \\ &=\left(\left(1-d_{j}^{u}\right)\left[1-\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right]-d_{j}^{u} \sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right) \theta_{j-1}^{u} \\ &=\left(1-d_{j}^{u}-\sigma\left(v(w)^{T} \theta_{j-1}^{u}\right)\right) \theta_{j-1}^{u} \end{aligned}

v(w)更新公式為:
v(w):=v(w)+\eta \sum_{u \in \text { Context }(w)} \sum_{j=2}^{l^{u}} \frac{\partial {O}\left(\theta_{j-1}^{u}, v(w)\right)}{\partial v(w)}

二昼接、算法實現(xiàn)

本次所用數(shù)據(jù)來自一家英國在線零售商店2010年1月12日至2011年12月9的全部交易數(shù)據(jù),數(shù)據(jù)規(guī)模為54908\times 8悴晰。下載地址慢睡,https://archive.ics.uci.edu/ml/datasets/Online+Retail ,字段描述如下:

  • InvoiceNo:發(fā)票編號铡溪。 定類數(shù)據(jù)漂辐,為每個事務唯一分配的6位整數(shù)。 如果此代碼以字母'c'開頭棕硫,則表示取消髓涯。
  • StockCode:產品(項目)代碼。 定類數(shù)據(jù)哈扮,為每個不同的產品唯一分配的5位整數(shù)纬纪。
  • Description:產品(項目)名稱。定類數(shù)據(jù)滑肉。
  • Quantity:每筆交易的每件產品(項目)的數(shù)量育八。 數(shù)字。
  • InvoiceDate:Invoice日期和時間赦邻。 數(shù)字,生成每個事務的日期和時間实檀。
  • UnitPrice:單價惶洲。 數(shù)字按声,英鎊單位產品價格。
  • CustomerID:客戶編號恬吕。 定類數(shù)據(jù)签则,為每個客戶唯一分配的5位整數(shù)。
  • Country:國家名稱铐料。 定類數(shù)據(jù)渐裂,每個客戶所在國家/地區(qū)的名稱。
數(shù)據(jù)集舉例
1. 庫函數(shù)钠惩、數(shù)據(jù)讀入以及刪除缺失值
import numpy as np
import pandas as pd
import random
from tqdm import tqdm
from gensim.models import Word2Vec
import matplotlib.pyplot as plt
import umap

data = pd.read_excel('Online Retail.xlsx')
data.isnull().sum()
data.dropna(inplace=True)
2. 找到用戶的個數(shù)及其列表柒凉,并隨機打亂,可知用戶共4372個
data['StockCode'] = data['StockCode'].astype(str)

customers = data['CustomerID'].unique().tolist()
random.shuffle(customers)

train_customers = [customers[i] for i in range(round(0.9*len(customers)))]
train_data = data[data['CustomerID'].isin(train_customers)]
validation_data = data[~data['CustomerID'].isin(train_customers)]
3. 劃分訓練集和驗證集并加載
train_customers = [customers[i] for i in range(round(0.9*len(customers)))]
train_data = data[data['CustomerID'].isin(train_customers)]
validation_data = data[~data['CustomerID'].isin(train_customers)]

train_purchases = []  #訓練集用戶購買記錄
for i in tqdm(train_customers):
    temp = train_data[train_data['CustomerID']==i]['StockCode'].tolist()
    train_purchases.append(temp)
    
val_purchases = []  #驗證集用戶購買記錄
for i in tqdm(validation_data['CustomerID'].unique()):
    temp = validation_data[validation_data['CustomerID']==i]['StockCode'].tolist()
    val_purchases.append(temp)
100%|██████████| 3935/3935 [00:04<00:00, 974.74it/s]
100%|██████████| 437/437 [00:00<00:00, 1613.25it/s]
4. word2vec模型加載及初始化
model = Word2Vec(window = 10,sg = 1, hs = 0, negative = 10, alpha = 0.03, min_alpha = 0.0007, seed = 14)
model.build_vocab(train_purchases,progress_per = 200)
model.train(train_purchases, total_examples = model.corpus_count, epochs = 10, report_delay = 1)

model.init_sims(replace=True)
X = model[model.wv.vocab]
5. 模型向量可視化
cluster_embedding = umap.UMAP(n_neighbors=30, min_dist=0.0, n_components=2, random_state=42).fit_transform(X)
plt.figure(1)
plt.scatter(cluster_embedding[:,0], cluster_embedding[:,1], s=3, cmap='Spectral')
6. 創(chuàng)建產品股票代碼及描述的字典并刪除重復項
products = train_data[["StockCode", "Description"]]
products.drop_duplicates(inplace=True, subset='StockCode', keep='last')
products_dict = products.groupby('StockCode')['Description'].apply(list).to_dict()
In [8]: products_dict['21931']
Out[8]: ['JUMBO STORAGE BAG SUKI']
7. 以產品向量作為輸入篓跛,并返回6個相似產品
def similar_products(v,n=6):
    ms = model.wv.similar_by_vector(v,topn = n+1)[1:]
    new_ms = []
    for j in ms:
        pair = (products_dict[j[0]][0],j[1])
        new_ms.append(pair)
    return new_ms
similar_products(model['84406B'])
In [9]: similar_products(model['21931'])
Out[9]: 
[('JUMBO BAG STRAWBERRY', 0.8357644081115723),
 ('JUMBO BAG OWLS', 0.8068020343780518),
 ('JUMBO  BAG BAROQUE BLACK WHITE', 0.7999265193939209),
 ('JUMBO BAG RED RETROSPOT', 0.7874696254730225),
 ('JUMBO BAG PINK POLKADOT', 0.7594423294067383),
 ('JUMBO STORAGE BAG SKULLS', 0.758986234664917)]
8. 基于多次購買的平均值推薦相似產品
def aggregate_vectors(products):
    product_vec = []
    for i in products:
        product_vec.append(model[i])
    return np.mean(product_vec,axis=0)
    
aggregate_vectors(val_purchases[1]).shape
similar_products(aggregate_vectors(val_purchases[1]))
similar_products(aggregate_vectors(val_purchases[1][-10:]))
In [13]: similar_products(aggregate_vectors(val_purchases[1]))
Out[13]: 
[('LUNCH BAG RED RETROSPOT', 0.6403661370277405),
 ('ALARM CLOCK BAKELIKE RED ', 0.638660728931427),
 ('RED RETROSPOT PICNIC BAG', 0.6361196637153625),
 ('JUMBO BAG RED RETROSPOT', 0.6360040903091431),
 ('SET/5 RED RETROSPOT LID GLASS BOWLS', 0.6345535516738892),
 ('ALARM CLOCK BAKELIKE PINK', 0.6296969056129456)]

In [14]: similar_products(aggregate_vectors(val_purchases[1][-10:]))
Out[14]: 
[('ROUND SNACK BOXES SET OF 4 FRUITS ', 0.7854548692703247),
 ('LUNCH BOX WITH CUTLERY RETROSPOT ', 0.6739486455917358),
 ('SET OF 3 BUTTERFLY COOKIE CUTTERS', 0.6696499586105347),
 ('SET OF 3 REGENCY CAKE TINS', 0.6598889827728271),
 ('PICNIC BOXES SET OF 3 RETROSPOT ', 0.6580283641815186),
 ('POSTAGE', 0.6528887748718262)]

參考資料

[1]. 推薦系統(tǒng)與深度學習. 黃昕等. 清華大學出版社. 2019.
[2]. 美團機器學習實踐. 美團算法團隊. 人民郵電出版社. 2018.
[3]. 推薦系統(tǒng)算法實踐. 黃美靈. 電子工業(yè)出版社. 2019.
[4]. 推薦系統(tǒng)算法. 項亮. 人民郵電出版社. 2012.
[5]. https://github.com/smrutiranjan097/Building-a-Recommendation-System-using-Word2vec
[6]. https://github.com/shoreyarchit/Movie-Recommendation-System
[7]. https://github.com/yhangf/ML-NOTE

夜雨翦春韭膝捞,新炊間黃粱±⒐担——杜甫《贈衛(wèi)八處士》

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末蔬咬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子沐寺,更是在濱河造成了極大的恐慌林艘,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件混坞,死亡現(xiàn)場離奇詭異狐援,居然都是意外死亡,警方通過查閱死者的電腦和手機拔第,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門咕村,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蚊俺,你說我怎么就攤上這事懈涛。” “怎么了泳猬?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵批钠,是天一觀的道長。 經常有香客問我得封,道長埋心,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任忙上,我火速辦了婚禮拷呆,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己茬斧,他們只是感情好腰懂,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著项秉,像睡著了一般绣溜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上娄蔼,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天怖喻,我揣著相機與錄音,去河邊找鬼岁诉。 笑死锚沸,一個胖子當著我的面吹牛,可吹牛的內容都是我干的唉侄。 我是一名探鬼主播咒吐,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼属划!你這毒婦竟也來了恬叹?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤同眯,失蹤者是張志新(化名)和其女友劉穎绽昼,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體须蜗,經...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡硅确,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了明肮。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菱农。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖柿估,靈堂內的尸體忽然破棺而出循未,到底是詐尸還是另有隱情,我是刑警寧澤秫舌,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布的妖,位于F島的核電站,受9級特大地震影響足陨,放射性物質發(fā)生泄漏嫂粟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一墨缘、第九天 我趴在偏房一處隱蔽的房頂上張望星虹。 院中可真熱鬧零抬,春花似錦、人聲如沸搁凸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽护糖。三九已至,卻和暖如春嚼松,著一層夾襖步出監(jiān)牢的瞬間嫡良,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工献酗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留寝受,地道東北人。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓罕偎,卻偏偏與公主長得像很澄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子颜及,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內容