自然語(yǔ)言處理 | 文本向量化

Tomas Mikolov2013年在ICLR提出用于獲取word vector的論文《Efficient estimation of word representations in vector space》,文中簡(jiǎn)單介紹了兩種訓(xùn)練模型CBOW、Skip-gram,以及兩種加速方法Hierarchical SoftmaxNegative Sampling。除了word2vec之外,還有其他的文本向量化的方法,因此在這里做個(gè)總結(jié)。

文本向量化(又稱“詞向量模型”、“向量空間模型”)即將文本表示成計(jì)算機(jī)可識(shí)別的實(shí)數(shù)向量,根據(jù)粒度大小不同可將文本特征表示分為字、詞、句子或篇章幾個(gè)層次。文本向量化的方法主要分為離散表示和分布式表示。

1.離散表示

一種基于規(guī)則和統(tǒng)計(jì)的向量化方式,常用的方法包括詞集模型詞袋模型,都是基于詞之間保持獨(dú)立性、沒(méi)有關(guān)聯(lián)為前提,將所有文本中單詞形成一個(gè)字典,然后根據(jù)字典來(lái)統(tǒng)計(jì)單詞出現(xiàn)頻數(shù),不同的是:

  • 詞集模型:例如One-Hot Representation,只要單個(gè)文本中單詞出現(xiàn)在字典中,就將其置為1,不管出現(xiàn)多少次
  • 詞袋模型:只要單個(gè)文本中單詞出現(xiàn)在字典中,就將其向量值加1,出現(xiàn)多少次就加多少次

其基本的特點(diǎn)是忽略了文本信息中的語(yǔ)序信息語(yǔ)境信息,僅將其反映為若干維度的獨(dú)立概念,這種情況有著因?yàn)槟P捅旧碓蚨鵁o(wú)法解決的問(wèn)題,比如主語(yǔ)和賓語(yǔ)的順序問(wèn)題,詞袋模型天然無(wú)法理解諸如“我為你鼓掌”和“你為我鼓掌”兩個(gè)語(yǔ)句之間的區(qū)別。

One-Hot Representation

將每個(gè)都表示成一個(gè)長(zhǎng)向量,向量的維度是詞表的大小,詞的當(dāng)前位置用1表示,其他位置用0表示。

import numpy as np
import pandas as pd
import jieba

def doc2onthot_matrix(file_path):
    # 讀取待編碼的文件
    with open(file_path, encoding="utf-8") as f:
        docs = f.readlines()
    with open(file_path1, encoding="utf-8") as f:
        docs1 = f.readlines()
    
    # 將文件每行分詞,分詞后的詞語(yǔ)放入words中
    words=[]
    for i in range(len(docs)):
        docs[i] = jieba.cut(docs[i].strip("\n"))
        words += docs[i]
    
    # 找出分詞后不重復(fù)的詞語(yǔ),作為詞袋,是后續(xù)onehot編碼的維度
    vocab = sorted(set(words), key=words.index)
    
    # 建立一個(gè)M行V列的全0矩陣,M是文檔樣本數(shù),這里是行數(shù),V為不重復(fù)詞語(yǔ)數(shù),即編碼維度
    V = len(vocab)
    M = len(docs)
    onehot = np.zeros((M,V))
    
    for i,doc in enumerate(docs1):
        words = ""
        for word in doc:
            if word != " ":
                words = words + word
                continue
            if words in vocab:
                pos = vocab.index(words)
                onehot[i][pos] = 1
                words = ""
            else:
                words = ""
                continue
    onehot=pd.DataFrame(onehot, columns=vocab)
    return onehot

file_path = "D:/Pythonworkspace/test.txt"
file_path1 = "D:/Pythonworkspace/word.txt"
onehot = doc2onthot_matrix(file_path)
onehot

One-Hot編碼的優(yōu)點(diǎn)是簡(jiǎn)單快捷,缺點(diǎn)是數(shù)據(jù)稀疏、耗時(shí)耗空間、不能很好地展示詞與詞之間的相似關(guān)系,且還未考慮到詞出現(xiàn)的頻率,因而無(wú)法區(qū)別詞的重要性。

One-Hot矩陣

詞袋模型

對(duì)于句子篇章而言,常用的離散表示方法是詞袋模型。詞袋模型以O(shè)ne-Hot為基礎(chǔ),忽略詞表中詞的順序和語(yǔ)法關(guān)系,通過(guò)記錄詞表中的每一個(gè)詞在該文本中出現(xiàn)的頻次來(lái)表示該詞在文本中的重要程度,解決了 One-Hot 未能考慮詞頻的問(wèn)題。
詞袋模型的優(yōu)點(diǎn)是方法簡(jiǎn)單,當(dāng)語(yǔ)料充足時(shí),處理簡(jiǎn)單的問(wèn)題如文本分類,其效果比較好。詞袋模型的缺點(diǎn)是數(shù)據(jù)稀疏、維度大,且不能很好地展示詞與詞之間的相似關(guān)系。

TF-IDF

TF-IDF(詞頻-逆文檔頻率法,Term Frequency–Inverse Document Frequency)作為一種加權(quán)方法,在詞袋模型的基礎(chǔ)上對(duì)詞出現(xiàn)的頻次賦予TF-IDF權(quán)值,對(duì)詞袋模型進(jìn)行修正,進(jìn)而表示該詞在文檔集合中的重要程度。

import numpy as np
import pandas as pd
import math
import jieba
import jieba.analyse
import re

# 中文分詞
def word_segment(file_path_before, file_path_after, stop_word_file,dic_file):
    # 讀取待編碼的文件
    with open(file_path_before,encoding="utf-8") as f:
        docs = f.readlines()
    
    # 加載停用詞
    stopwords=[]
    for word in open(stop_word_file, 'r'): # 這里加載停用詞的路徑
        stopwords.append(word.strip("\n"))

    words_list = []
    for i,text in enumerate(docs):
        words = []
        final_word = []
        p = re.compile(r"\n|:|;|,|、|(|)|\.|。|,|/|(\|)", re.S)
        text = p.sub('', text)
        jieba.load_userdict(dic_file)
        word = jieba.cut(text)
        words += word
        for i,word in enumerate(words):
            if word not in stopwords:
                final_word.append(word)
        words_list.append(final_word)
    
    for i in range(0,len(words_list)):
        with open(file_path_after, 'a') as f1:
            f1.write(str(words_list[i]))
            f1.write("\n")

    return words_list

# 生成TD-IDF矩陣
def doc2tfidf_matrix(file_path_before, words_list):     
    # 找出分詞后不重復(fù)的詞語(yǔ),作為詞袋
    words = []
    for i,word in enumerate(words_list):
        words += word
    vocab = sorted(set(words),key=words.index)
    # print(vocab)
    
    # 建立一個(gè)M行V列的全0矩陣,M是文檔樣本數(shù),這里是行數(shù),V為不重復(fù)詞語(yǔ)數(shù),即編碼維度
    V = len(vocab)
    M = len(words_list)
    onehot = np.zeros((M,V)) # 二維矩陣要使用雙括號(hào)
    tf = np.zeros((M,V))
    
    for i,doc in enumerate(words_list):
        for word in doc:
            if word in vocab:
                pos = vocab.index(word)
                # print(pos,word)
                onehot[i][pos] = 1
                tf[i][pos] += 1 # tf,統(tǒng)計(jì)某詞語(yǔ)在一條樣本中出現(xiàn)的次數(shù)
            else:
                print(word)
    
    row_sum = tf.sum(axis=1) # 行相加,得到每個(gè)樣本出現(xiàn)的詞語(yǔ)數(shù)
    # 計(jì)算TF(t,d)
    tf = tf/row_sum[:, np.newaxis] #分母表示各樣本出現(xiàn)的詞語(yǔ)數(shù),tf為單詞在樣本中出現(xiàn)的次數(shù),[:,np.newaxis]作用類似于行列轉(zhuǎn)置
    # 計(jì)算DF(t,D),IDF
    df = onehot.sum(axis=0) # 列相加,表示有多少樣本包含詞袋某詞
    idf = list(map(lambda x:math.log10((M+1)/(x+1)),df))
    # 計(jì)算TFIDF
    tfidf = tf*np.array(idf)
    tfidf = pd.DataFrame(tfidf,columns=vocab)
    return tfidf

file_path_before = "C:/Users/asus/Desktop/goodat.txt"
file_path_after = "C:/Users/asus/Desktop/word segment.txt"
stop_word_file = "C:/Users/asus/Desktop/stop_word.txt"
dic_file = "C:/Users/asus/Desktop/dic.txt"

words_list = word_segment(file_path_before, file_path_after,stop_word_file,dic_file)
tfidf = doc2tfidf_matrix(file_path_after, words_list)
for i in range(0,50):
    goodat = ""
    row = tfidf.iloc[i].sort_values(ascending=False)
    for i in range(0,10):
        goodat = goodat + row.index[i] + "/"
    print(goodat)

在利用TF-IDF進(jìn)行特征提取時(shí),若詞α在某篇文檔中出現(xiàn)頻率較高且在其他文檔中出現(xiàn)頻率較低時(shí),則認(rèn)為α可以代表該文檔的特征,具有較好的分類能力,那么α作為特征被提取出來(lái)。

TF-IDF矩陣
CountVectorizer

CountVectorizer 根據(jù)文本構(gòu)建出一個(gè)詞表,詞表中包含了所有文本中的單詞,每一個(gè)詞匯對(duì)應(yīng)其出現(xiàn)的順序,構(gòu)建出的詞向量的每一維都代表這一維對(duì)應(yīng)單詞出現(xiàn)的頻次,這些詞向量組成的矩陣稱為頻次矩陣。但 CountVectorizer 只能表達(dá)詞在當(dāng)前文本中的重要性,無(wú)法表示該詞在整個(gè)文檔集合中的重要程度。

TF-idfVectorizer

TF-idfVectorizer 則在CountVectorizer 的基礎(chǔ)上計(jì)算了詞的詞頻和逆文檔頻率,將詞的 TF-IDF 權(quán)值作為該詞對(duì)應(yīng)維度的值以區(qū)分詞的重要性。相比之下,數(shù)據(jù)集越大,利用 TF-idfVectorizer 進(jìn)行文本向量化就更有優(yōu)勢(shì)。

潛語(yǔ)義分析模型(Latent Semantic Analysis)

文檔的詞向量空間轉(zhuǎn)化為語(yǔ)義級(jí)別的向量空間,使用主成分分析方法降維,抽取向量空間內(nèi)分布方差最大的若干個(gè)正交方向來(lái)作為最后的表示方向,并對(duì)其余方向的內(nèi)容進(jìn)行丟棄。

SVD奇異值分解
PPMI

2.分布式表示

每個(gè)詞根據(jù)上下文從高維映射到一個(gè)低維度、稠密的向量上,向量的維度需要指定。在構(gòu)成的向量空間中,每個(gè)詞的含義都可以用周邊的詞來(lái)表示,優(yōu)點(diǎn)是考慮到了詞之間存在的相似關(guān)系,減小了詞向量的維度。常用的方法包括:

  • 基于矩陣的分布表示
  • 基于聚類的分布表示
  • 基于神經(jīng)網(wǎng)絡(luò)的分布表示,其特點(diǎn)是:①利用了激活函數(shù)及softmax函數(shù)中的非線性特點(diǎn) ②保留了語(yǔ)序信息

Word2vec

Word2vec是Google的開(kāi)源項(xiàng)目,其特點(diǎn)是將所有的詞向量化,這樣詞與詞之間就可以定量度量。Word2vec以詞嵌入為基礎(chǔ),利用深度學(xué)習(xí)的思想,對(duì)出現(xiàn)在上下文環(huán)境中的詞進(jìn)行預(yù)測(cè),經(jīng)過(guò)Word2vec訓(xùn)練后的詞向量可以很好度量詞與詞之間的相似性,將所有詞語(yǔ)投影到k維的向量空間,每個(gè)詞語(yǔ)都可以用一個(gè)k維向量表示,進(jìn)而將文本內(nèi)容的處理簡(jiǎn)化為K維向量空間中的向量運(yùn)算。

已有的預(yù)訓(xùn)練詞向量:騰訊AI實(shí)驗(yàn)室 Embedding Dataset,該語(yǔ)料庫(kù)為超過(guò)800萬(wàn)個(gè)中文單詞和短語(yǔ)提供了200維矢量表示,這些單詞和短語(yǔ)是在大規(guī)模高質(zhì)量數(shù)據(jù)上預(yù)先訓(xùn)練的。

Word2vec有Continuous Bag-of-Words(CBOW)Skip-gram兩種訓(xùn)練模型,這兩種模型可以看做簡(jiǎn)化的三層神經(jīng)網(wǎng)絡(luò),主要包括輸入層、隱藏層以及輸出層。CBOW對(duì)小型數(shù)據(jù)庫(kù)比較合適,而Skip-Gram在大型語(yǔ)料中表現(xiàn)更好。

  • CBOW:訓(xùn)練輸入是某一個(gè)特征詞上下文相關(guān)的詞對(duì)應(yīng)的詞向量,輸出就是這特定的一個(gè)詞的詞向量。
CBOW
  • Skip-gram:和CBOW模型相反,輸入是中心詞,輸出是上下文。
Skip-gram
import jieba
from gensim.models import word2vec

# 去掉標(biāo)點(diǎn)符號(hào)
def clearSen(comment):
    comment = ' '.join(comment).replace(',', '').replace('。', '').replace('?', '').replace('“', '').replace('”', '').replace('.', '').replace('(', '').replace(':', '').replace('…', '').replace('...', '').replace('、', '')
    return comment

# 讀取數(shù)據(jù)并分詞
file_path = "D:/Pythonworkspace/test.txt"
with open(file_path,encoding="utf-8") as f:
    docs  = f.readlines()
docs = clearSen(docs)
words = ' '.join(jieba.cut(docs))

with open("D:/Pythonworkspace/word.txt",'w',encoding='utf-8') as f:
    f.write(words)

sentences = word2vec.Text8Corpus(r'D:/Pythonworkspace/word.txt')
# 基于當(dāng)前語(yǔ)料集訓(xùn)練模型
model = word2vec.Word2Vec(sentences, size=50, window=5, workers=4, min_count=3)

# 獲取詞向量
print(model['骨科'])

# 查看向量化之后的距離
for i in model.most_similar(u"骨科"): 
    print(i[0],i[1])

gensim函數(shù)庫(kù)訓(xùn)練Word2Vec模型有很多配置參數(shù),這里進(jìn)行簡(jiǎn)單說(shuō)明:
class gensim.models.word2vec.Word2Vec(sentences=None,size=100,alpha=0.025,window=5, min_count=5, max_vocab_size=None, sample=0.001,seed=1, workers=3,min_alpha=0.0001, sg=0, hs=0, negative=5, cbow_mean=1, hashfxn=<built-in function hash>,iter=5,null_word=0, trim_rule=None, sorted_vocab=1, batch_words=10000)
參數(shù):

  • sentences:可以是一個(gè)list,對(duì)于大語(yǔ)料集,建議使用BrownCorpus,Text8Corpus或LineSentence構(gòu)建。
  • sg: 用于設(shè)置訓(xùn)練算法,默認(rèn)為0,對(duì)應(yīng)CBOW算法;sg=1則采用skip-gram算法。
  • size:是指特征向量的維度,默認(rèn)為100。值太小會(huì)導(dǎo)致詞映射因?yàn)闆_突而影響結(jié)果,值太大則會(huì)耗內(nèi)存并使算法計(jì)算變慢,一般值取為100到200之間
  • window:句子中當(dāng)前詞與目標(biāo)詞之間的最大距離,3表示在目標(biāo)詞前看3-b個(gè)詞,后面看b個(gè)詞(b在0-3之間隨機(jī))
  • alpha: 學(xué)習(xí)速率
  • seed:用于隨機(jī)數(shù)發(fā)生器,與初始化詞向量有關(guān)。
  • min_count: 可以對(duì)字典做截?cái)?,詞頻少于min_count次數(shù)的單詞會(huì)被丟棄掉,默認(rèn)值為5
  • max_vocab_size: 設(shè)置詞向量構(gòu)建期間的RAM限制。如果所有獨(dú)立單詞個(gè)數(shù)超過(guò)這個(gè),則就消除掉其中最不頻繁的一個(gè)。每一千萬(wàn)個(gè)單詞需要大約1GB的RAM。設(shè)置成None則沒(méi)有限制。
  • sample: 高頻詞匯的隨機(jī)降采樣的配置閾值,默認(rèn)為1e-3,范圍是(0,1e-5)
  • workers: 參數(shù)控制訓(xùn)練的并行數(shù)。
  • hs: 如果為1則會(huì)采用hierarchica·softmax技巧。如果設(shè)置為0(defau·t),則negative sampling會(huì)被使用。
  • negative: 如果>0,則會(huì)采用negativesampling,用于設(shè)置多少個(gè)noise words
  • cbow_mean: 如果為0,則采用上下文詞向量的和,如果為1(default)則采用均值。只有使用CBOW的時(shí)候才起作用。
  • hashfxn: hash函數(shù)來(lái)初始化權(quán)重。默認(rèn)使用python的hash函數(shù)
  • iter: 迭代次數(shù),默認(rèn)為5
  • trim_rule: 用于設(shè)置詞匯表的整理規(guī)則,指定那些單詞要留下,哪些要被刪除??梢栽O(shè)置為None(min_count會(huì)被使用)或者一個(gè)接受()并返回RU·E_DISCARD,uti·http://s.RU·E_KEEP或者uti·http://s.RU·E_DEFAU·T的函數(shù)。
  • sorted_vocab:如果為1(defau·t),則在分配word index 的時(shí)候會(huì)先對(duì)單詞基于頻率降序排序。
  • batch_words:每一批的傳遞給線程的單詞的數(shù)量,默認(rèn)為10000

在上面的代碼中,使用了gensim庫(kù)中的word2vec,對(duì)分好詞后的語(yǔ)料集進(jìn)行向量化,并可以計(jì)算出詞與詞之間的相似性,例如在該語(yǔ)料集中,與“骨科”最相似的詞有:

doc2vec

PV-DM(Distributed Memory version of Paragraph Vector)

沿用了word2vec中CBOW的方法,通過(guò)一個(gè)單層的簡(jiǎn)單神經(jīng)網(wǎng)絡(luò)來(lái)構(gòu)建模型,在構(gòu)建隱藏層時(shí)將段落向量也加入隱藏層,并且與其他的詞向量一起獲得反向傳播的梯度

PV-DM
PV-DBOW(Distributed Bags of Words version of Paragraph Vector)

沿用了word2vec中Skip-gram的方法,以文檔向量作為輸入,以最大化輸出文檔中的詞作為目標(biāo)進(jìn)行訓(xùn)練。

PV-DBOW

GloVec

附:通過(guò)已保存的訓(xùn)練好的詞向量模型,得到word embedding

# load the pre-trained word-embedding vectors
embeddings_index = {}
for i, line in enumerate(open('wiki-news-300d-1M.vec', 'r', encoding='UTF-8')):
    values= line.split()
    embeddings_index[values[0]] = np.asarray(values[1:], dtype='float32')

len(embeddings_index) # 999995

# create atokenizer
token = text.Tokenizer()
token.fit_on_texts(自己的語(yǔ)料)
word_index = token.word_index # 單詞對(duì)應(yīng)的index
index_docs = token.index_docs # index對(duì)應(yīng)單詞出現(xiàn)的文檔的數(shù)量
word_docs = token.word_docs # 單詞出現(xiàn)的文檔的數(shù)量
word_counts = token.word_counts # 單詞在所有文檔中出現(xiàn)的總次數(shù)

# convert text tosequence of tokens andpad them toensure equal length vectors
train_seq_x = sequence.pad_sequences(token.texts_to_sequences(Train_X_abs))
test_seq_x = sequence.pad_sequences(token.texts_to_sequences(Test_X_abs))

# create token-embedding mapping
# embedding_matrix = np.zeros((len(word_index) + 1, 300))
embedding_matrix = pd.DataFrame()
for word, i in word_index.items():
    word = word.replace("'","")
    embedding_vector = embeddings_index.get(word)
    if embedding_vector is not None:
        embedding_matrix[word] = embedding_vector

print(embedding_matrix['system'])
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,024評(píng)論 3 413
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,285評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,548評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,328評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,878評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,098評(píng)論 0 286
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,616評(píng)論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,554評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,725評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,971評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,361評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,613評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,339評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,695評(píng)論 2 370

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