《機(jī)器學(xué)習(xí)》第四章:決策樹

簡述

決策樹是機(jī)器學(xué)習(xí)的一類常見算法谣辞,其核心思想是通過構(gòu)建一個樹狀模型來對新樣本進(jìn)行預(yù)測。樹的葉結(jié)點(diǎn)是預(yù)測結(jié)果沐扳,而所有非葉結(jié)點(diǎn)皆是一個個決策過程泥从。決策樹很多任務(wù)都 是為了數(shù)據(jù)中所蘊(yùn)含的知識信息,因此決策樹可以使用不熟悉的數(shù)據(jù)集合沪摄,并從中提取出一系列規(guī)則躯嫉,機(jī)器學(xué)習(xí)算法最終將使用這些機(jī)器從數(shù)據(jù)集中創(chuàng)造的規(guī)則。

image

ps:決策樹實(shí)際上就是一個將數(shù)據(jù)按屬性分堆的過程杨拐。雖然名字叫的挺高大上的祈餐,但是背后的邏輯卻十分簡單。(其實(shí)大多數(shù)算法的思想都很簡單戏阅,大家不要被復(fù)雜的公式嚇到)昼弟,那么它較難的地方就不在于這個背后邏輯了啤它,而在于奕筐,比如我們是根據(jù)什么策略來決定屬性測試的順序的,因?yàn)槲颐磳傩缘膬?yōu)先級有特殊偏好变骡;選定屬性的取值的標(biāo)準(zhǔn)是什么离赫;構(gòu)建的決策樹是否足夠泛化等問題。

決策樹代碼


from math import log

import operator

def calcShannonEnt(dataSet):  # 計(jì)算數(shù)據(jù)的熵(entropy)

    numEntries=len(dataSet)  # 數(shù)據(jù)條數(shù)

    labelCounts={}

    for featVec in dataSet:

        currentLabel=featVec[-1] # 每行數(shù)據(jù)的最后一個字(類別)

        if currentLabel not in labelCounts.keys():

            labelCounts[currentLabel]=0

        labelCounts[currentLabel]+=1  # 統(tǒng)計(jì)有多少個類以及每個類的數(shù)量

    shannonEnt=0

    for key in labelCounts:

        prob=float(labelCounts[key])/numEntries # 計(jì)算單個類的熵值

        shannonEnt-=prob*log(prob,2) # 累加每個類的熵值

    return shannonEnt

def createDataSet1():    # 創(chuàng)造示例數(shù)據(jù)

    dataSet = [['青綠' , '蜷縮', '濁響', '清晰', '凹陷', '硬滑', '好瓜'],

              ['烏黑' , '蜷縮' , '沉悶' , '清晰' , '凹陷' , '硬滑' , '好瓜'] ,

              ['烏黑' , '蜷縮' , '濁響' , '清晰' , '凹陷' , '硬滑' , '好瓜'] ,

              ['青綠' , '蜷縮' , '沉悶' , '清晰' , '凹陷' , '硬滑' , '好瓜'] ,

              ['淺白' , '蜷縮' , '濁響' , '清晰' , '凹陷' , '硬滑' , '好瓜'] ,

              ['青綠' , '稍縮' , '濁響' , '清晰' , '稍凹' , '軟粘' , '好瓜'] ,

              ['烏黑' , '稍縮' , '濁響' , '稍糊' , '稍凹' , '軟粘' , '好瓜'] ,

              ['烏黑' , '稍縮' , '濁響' , '清晰' , '稍凹' , '硬滑' , '好瓜'] ,

              ['烏黑' , '稍縮' , '沉悶' , '稍糊' , '稍凹' , '硬滑' , '好瓜'] ,

              ['青綠' , '硬挺' , '清脆' , '清晰' , '平坦' , '硬滑' , '壞瓜'] ,

              ['淺白' , '硬挺' , '清脆' , '模糊' , '平坦' , '軟粘' , '壞瓜'] ,

              ['淺白' , '蜷縮' , '濁響' , '模糊' , '平坦' , '硬滑' , '壞瓜'] ,

              ['青綠' , '稍縮' , '濁響' , '稍糊' , '凹陷' , '軟粘' , '壞瓜'] ,

              ['淺白' , '稍縮' , '沉悶' , '稍糊' , '凹陷' , '硬滑' , '壞瓜'] ,

              ['烏黑' , '稍縮' , '濁響' , '清晰' , '稍凹' , '軟粘' , '壞瓜'] ,

              ['淺白' , '蜷縮' , '濁響' , '模糊' , '平坦' , '硬滑' , '壞瓜'] ,

              ['青綠' , '蜷縮' , '沉悶' , '稍糊' , '稍凹' , '硬滑' , '壞瓜'] ]

    labels = ['色澤', '根蒂', '敲聲', '紋理', '臍部', '觸感']  #6個特征

    return dataSet,labels

def splitDataSet(dataSet,axis,value): # 按某個特征分類后的數(shù)據(jù)

    retDataSet=[]

    for featVec in dataSet:

        if featVec[axis]==value:

            reducedFeatVec =featVec[:axis]

            reducedFeatVec.extend(featVec[axis+1:])

            retDataSet.append(reducedFeatVec)

    return retDataSet

def chooseBestFeatureToSplit(dataSet):  # 選擇最優(yōu)的分類特征

    numFeatures = len(dataSet[0])-1

    baseEntropy = calcShannonEnt(dataSet)  # 原始的熵

    bestInfoGain = 0

    bestFeature = -1

    for i in range(numFeatures):

        featList = [example[i] for example in dataSet]

        uniqueVals = set(featList)

        newEntropy = 0

        for value in uniqueVals:

            subDataSet = splitDataSet(dataSet,i,value)

            prob =len(subDataSet)/float(len(dataSet))

            newEntropy +=prob*calcShannonEnt(subDataSet)  # 按特征分類后的熵

        infoGain = baseEntropy - newEntropy  # 原始熵與按特征分類后的熵的差值

        if (infoGain>bestInfoGain):  # 若按某特征劃分后塌碌,熵值減少的最大渊胸,則次特征為最優(yōu)分類特征

            bestInfoGain=infoGain

            bestFeature = i

    return bestFeature

def majorityCnt(classList):    #按分類后類別數(shù)量排序,比如:最后分類為2好瓜1壞瓜台妆,則判定為好瓜翎猛;

    classCount={}

    for vote in classList:

        if vote not in classCount.keys():

            classCount[vote]=0

        classCount[vote]+=1

    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)

    return sortedClassCount[0][0]

def createTree(dataSet,labels):

    classList=[example[-1] for example in dataSet]  # 類別:好瓜或壞瓜

    if classList.count(classList[0])==len(classList):

        return classList[0]

    if len(dataSet[0])==1:

        return majorityCnt(classList)

    bestFeat=chooseBestFeatureToSplit(dataSet) #選擇最優(yōu)特征

    bestFeatLabel=labels[bestFeat]

    myTree={bestFeatLabel:{}} #分類結(jié)果以字典形式保存

    del(labels[bestFeat])

    featValues=[example[bestFeat] for example in dataSet]

    uniqueVals=set(featValues)

    for value in uniqueVals:

        subLabels=labels[:]

        myTree[bestFeatLabel][value]=createTree(splitDataSet\

                            (dataSet,bestFeat,value),subLabels)

    return myTree

if __name__=='__main__':

    dataSet, labels=createDataSet1()  # 創(chuàng)造示列數(shù)據(jù)

    print(createTree(dataSet, labels))  # 輸出決策樹模型結(jié)果

這里參考了andy老師的源碼胖翰,參考資料:https://gitee.com/ZHBIT-MachineLearning/Machine-Learning-Base/blob/master/Fourth_DecisionTree/Fourth_DecisionTree.py#

知識點(diǎn)

1、信息熵

“信息熵”(information entropy)是度量樣本集合純度最常用的一種指標(biāo)切厘。其代表的是不同的類別在數(shù)據(jù)在總數(shù)據(jù)集中的熵的和萨咳,當(dāng)樣本集合越“純”時,信息熵越幸吒濉(信息熵的取值范圍為0~log2(k))培他。

2、信息增益

原始信息熵與新的劃分后的信息墑之差就是這次屬性劃分的“信息增益”(information gain)遗座,所以我們的屬性劃分選擇就是在每個結(jié)點(diǎn)找到最高信息增益的屬性舀凛。

3、增益率

增益率是讓信息增益除以選擇的劃分屬性的“固有值”(intrinsic value)途蒋。一般來說屬性a的可能取值數(shù)目越多猛遍,則固有值越大。

4号坡、基尼指數(shù)

原始基尼值與新劃分后的不同樣本集合的基尼值之和的差值就是“基尼指數(shù)”(Gini index)螃壤,而基尼值的公式如下:

Gini(D) = 1 - pk2

基尼值反映了從數(shù)據(jù)集D中隨機(jī)抽取兩個樣本,其類別標(biāo)記不一致的概率筋帖。因此奸晴,基尼值越小,則數(shù)據(jù)集D的純度越高日麸。

決策樹的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

1寄啼、計(jì)算量簡單,可解釋性強(qiáng)代箭,比較適合處理有缺失屬性值的樣本墩划,能夠處理不相關(guān)的特征;

缺點(diǎn):

1嗡综、單顆決策樹分類能力弱乙帮,并且對連續(xù)值變量難以處理;

2极景、容易過擬合(后續(xù)出現(xiàn)了隨機(jī)森林察净,減小了過擬合現(xiàn)象);

決策樹構(gòu)建步驟

我們在劃分中盼樟,希望決策樹的分支結(jié)點(diǎn)所包含的樣本盡可能屬于同一類別氢卡,即結(jié)點(diǎn)的“純度”(purity)越來越高。要使得結(jié)點(diǎn)純度越來越高晨缴,我們得要選擇合適的指標(biāo)來對純度進(jìn)行評估译秦,并且我們也得要在這些指標(biāo)上對屬性的劃分進(jìn)行選擇。目前決策樹主流使用的屬性劃分指標(biāo)為以下三個(如何量化純度):

[1]信息增益

"信息熵" (information entropy)是度量樣本集合純度最常用的一種指標(biāo).假定當(dāng)前樣本集合 D 中第 k 類樣本所占的比例為 Pk

(k = 1, 2,. . . , IYI) ,則 D的信息墑定義為:

image

屬性劃分邏輯是這樣的:信息熵代表的是樣本集合的純度筑悴,當(dāng)用一個屬性對樣本進(jìn)行劃分后们拙,分開的不同樣本集合的信息墑之和純度最高,那么這個屬性就是目前最好的劃分選擇阁吝。

如何度量數(shù)據(jù)集的無序程度的方式是計(jì)算信息熵睛竣。

信息增益(information gain)=原始信息熵-新的劃分后的信息墑,所以我們的屬性劃分選擇就是在每個結(jié)點(diǎn)找到最高信息增益的屬性求摇。

[2]增益率

增益率是讓信息增益除以選擇的劃分屬性的“固有值”(intrinsic value)射沟。

image

為何要引入增益率?因?yàn)樵鲆媛适且粋€增量△值与境,引入增益率可以解決信息增益對可取值數(shù)目較多的屬性有所偏好的問題验夯,更加正確的描述樣本集合純度。

然而信息增益除以固有值后獲得的增益率反而是偏愛取值數(shù)目較少的屬性的摔刁。

解決辦法:先從候選劃分屬性中選出信息增益高于平均水平的屬性挥转,再從中選擇增益率最高的。

[3]基尼指數(shù)

基尼值的公式如下:

image

從上式可以看出共屈,基尼值反映了從數(shù)據(jù)集D中隨機(jī)抽取兩個樣本绑谣,其類別標(biāo)記不一致的概率。因此拗引,基尼值越小借宵,則數(shù)據(jù)集D的純度越高。

那么同信息增益的選擇策略一樣:當(dāng)用一個屬性對樣本進(jìn)行劃分后矾削,原始基尼值與新劃分后的不同樣本集合的基尼值之和的差值就是“基尼指數(shù)”(Gini index)壤玫,所以我們的屬性劃分選擇就是在每個結(jié)點(diǎn)找到劃分后最高基尼指數(shù)的屬性。

[4]剪枝處理

剪枝(pruning)是決策樹學(xué)習(xí)算法對付“過擬合”的主要手段哼凯。為了盡可能正確分類訓(xùn)練樣本欲间,會不斷的進(jìn)行結(jié)點(diǎn)劃分,有時會造成決策樹分支過多断部,這時就可能因?yàn)橛?xùn)練過程學(xué)得太好了猎贴,將訓(xùn)練集自身的一些特點(diǎn)當(dāng)作普遍化特征進(jìn)行學(xué)習(xí)而會導(dǎo)致過擬合。因此蝴光,可通過主動去掉一些分支來降低過擬合的風(fēng)險她渴。目前決策樹剪枝的基本策略有以下兩種:

前置剪枝(預(yù)剪枝):在分裂節(jié)點(diǎn)的時候設(shè)計(jì)比較苛刻的條件,如不滿足則直接停止分裂(這樣干決策樹無法到最優(yōu)虱疏,也無法得到比較好的效果)

后置剪枝(后剪枝):在樹建立完之后惹骂,用單個節(jié)點(diǎn)代替子樹苏携,節(jié)點(diǎn)的分類采用子樹中主要的分類(這種方法比較浪費(fèi)前面的建立過程)

減枝過程:使用第二章中提到的性能評估方法來比較泛化性能的改變:留出法做瞪、交叉驗(yàn)證法、自助法等。

從樣本預(yù)留出一部分?jǐn)?shù)據(jù)用作“驗(yàn)證集”以進(jìn)行性能評估装蓬。

image
image

由圖可看出著拭,頭結(jié)點(diǎn)臍部,精度由43.9%提高到71.4%牍帚,高于其他屬性儡遮,因此選擇臍部為頭結(jié)點(diǎn),其他分支同理暗赶。第二個枝葉鄙币,選取色澤,精度從71.4%降低到了57.1%蹂随,所以該枝葉別減十嘿。


補(bǔ)充:剪枝代碼詳細(xì)解釋+周志華《機(jī)器學(xué)習(xí)》決策樹圖4.5、圖4.6岳锁、圖4.7繪制

部分代碼如下:


#這個用于預(yù)剪枝

deftesting_feat(feat,train_data,test_data,labels):

print"train_data=",json.dumps(train_data,ensure_ascii=False)

class_list=[example[-1]forexampleintrain_data]

bestFeatIndex=labels.index(feat)

train_data=[example[bestFeatIndex]forexampleintrain_data]

test_data=[(example[bestFeatIndex],example[-1])forexampleintest_data]

all_feat=set(train_data)

error=0.0

forvalueinall_feat:

class_feat=[class_list[i]foriinrange(len(class_list))iftrain_data[i]==value]

major=majorityCnt(class_feat)

fordataintest_data:

ifdata[0]==valueanddata[1]!=major:

error+=1.0

# print 'myTree %d' % error

returnerror

#這個函數(shù)用于預(yù)剪枝和后剪枝

deftestingMajor(major,data_test):

error=0.0

foriinrange(len(data_test)):

ifmajor!=data_test[i][-1]:

error+=1

# print 'major %d' % error

returnfloat(error)

#這個函數(shù)專門用于"后剪枝"

deftesting(myTree,data_test,labels):

#這里輸入的labels不是全部的特征名稱

#這里輸入的data_test不帶有全部的特征名稱

print"----------進(jìn)入testing函數(shù)--------------"

print"data_test=",json.dumps(data_test,ensure_ascii=False)

print"labels=",json.dumps(labels,ensure_ascii=False)

error=0.0

foriinrange(len(data_test)):

ifclassify(myTree,labels,data_test[i])!=data_test[i][-1]:#如果預(yù)測結(jié)果與驗(yàn)證數(shù)據(jù)的類別標(biāo)簽不一致

error+=1#那么錯誤數(shù)就+1

print('myTree %d'%error)

returnfloat(error)

參考資料:https://blog.csdn.net/appleyuchi/article/details/83041047

運(yùn)行結(jié)果:

image

ps:減少過擬合現(xiàn)象的另一種方法:構(gòu)建隨機(jī)森林RF

參考 http://www.reibang.com/p/d792279a30bc

隨機(jī)森林是有很多隨機(jī)得決策樹構(gòu)成绩衷,它們之間沒有關(guān)聯(lián)。得到RF以后激率,在預(yù)測時分別對每一個決策樹進(jìn)行判斷咳燕,最后使用Bagging的思想進(jìn)行結(jié)果的輸出(也就是投票的思想)

學(xué)習(xí)過程

1、現(xiàn)在有N個訓(xùn)練樣本乒躺,每個樣本的特征為M個招盲,需要建K顆樹

2、從N個訓(xùn)練樣本中有放回的取N個樣本作為一組訓(xùn)練集(其余未取到的樣本作為預(yù)測分類嘉冒,評估其誤差)

3宪肖、從M個特征中取m個特征左右子集特征(m<

4、對采樣的數(shù)據(jù)使用完全分裂的方式來建立決策樹健爬,這樣的決策樹每個節(jié)點(diǎn)要么無法分裂控乾,要么所有的樣本都指向同一個分類

5、重復(fù)2的過程K次娜遵,即可建立森林


[5]連續(xù)與缺失值

那么當(dāng)遇到屬性是連續(xù)或缺失時蜕衡,我們應(yīng)該知道該如何處理?

連續(xù)值處理

遇到連續(xù)值屬性時處理邏輯是:將連續(xù)值轉(zhuǎn)換為離散值设拟。我們需要做的是將訓(xùn)練樣本中該屬性的所有取值進(jìn)行排序慨仿,并對這排好序的取值隊(duì)列進(jìn)行分區(qū)劃分,每一個分區(qū)即為該屬性的一個離散點(diǎn)取值纳胧。

我們?nèi)《址ǎ捶譃閮蓚€分區(qū)镰吆,可以根據(jù)需要分為N個)來進(jìn)行描述。將區(qū)間的中位點(diǎn)作為候選劃分點(diǎn):

image

根據(jù)不同的劃分跑慕,我們可以分別計(jì)算一下信息增益的大小万皿,然后選出一個最好的劃分來摧找。

需注意的是,和離散值不同牢硅,連續(xù)值的劃分是可以在子結(jié)點(diǎn)上繼續(xù)劃分的蹬耘。即你將身高劃分為“小于175”和“大于等于175”兩部分,對于“小于175”的子結(jié)點(diǎn)减余,你仍然可以繼續(xù)劃分為“小于160”和“大于160”兩部分综苔。

[6]缺失值處理

屬性值缺失的情況下進(jìn)行屬性劃分選擇

不將缺失值的樣本代入選擇判斷的公式計(jì)算(信息增益、增益率位岔、基尼指數(shù))之中如筛,只在計(jì)算完后乘以一個有值的樣本比例即可。

比如訓(xùn)練集有10個樣本抒抬,在屬性 a 上妙黍,有兩個樣本缺失值,那么計(jì)算該屬性劃分的信息增益時瞧剖,我們可以忽略這兩個缺失值的樣本來計(jì)算信息增益拭嫁,然后在計(jì)算結(jié)果上乘以8/10即可。

本在劃分屬性上的值為時節(jié)點(diǎn)的分配

若樣本 x 在劃分屬性 a 上取值未知抓于,則將 x 劃入所有子結(jié)點(diǎn)做粤,但是對劃入不同子結(jié)點(diǎn)中的 x 賦予不同的權(quán)值(不同子結(jié)點(diǎn)上的不同權(quán)值一般體現(xiàn)為該子結(jié)點(diǎn)所包含的數(shù)據(jù)占父結(jié)點(diǎn)數(shù)據(jù)集合的比例)。

[7]多變量決策樹

在此類決策樹中捉撮,非葉結(jié)點(diǎn)不再是僅對某個屬性怕品,而是對屬性的線性組合進(jìn)行測試;換言之巾遭,每個非葉結(jié)點(diǎn)時一個形如線性回歸的線性分類器了肉康。下圖是一個在連續(xù)屬性密度和含糖率上的選瓜多變量決策樹樣例:

image
image

python構(gòu)建決策樹這篇文章總結(jié)的很好:http://www.reibang.com/p/e7ed734b8fcb

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市灼舍,隨后出現(xiàn)的幾起案子吼和,更是在濱河造成了極大的恐慌,老刑警劉巖骑素,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炫乓,死亡現(xiàn)場離奇詭異,居然都是意外死亡献丑,警方通過查閱死者的電腦和手機(jī)末捣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來创橄,“玉大人箩做,你說我怎么就攤上這事⊥孜罚” “怎么了邦邦?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵安吁,是天一觀的道長。 經(jīng)常有香客問我圃酵,道長柳畔,這世上最難降的妖魔是什么馍管? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任郭赐,我火速辦了婚禮,結(jié)果婚禮上确沸,老公的妹妹穿的比我還像新娘捌锭。我一直安慰自己,他們只是感情好罗捎,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布观谦。 她就那樣靜靜地躺著,像睡著了一般桨菜。 火紅的嫁衣襯著肌膚如雪豁状。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天倒得,我揣著相機(jī)與錄音泻红,去河邊找鬼。 笑死霞掺,一個胖子當(dāng)著我的面吹牛谊路,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播菩彬,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼缠劝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了骗灶?” 一聲冷哼從身側(cè)響起惨恭,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎耙旦,沒想到半個月后喉恋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡母廷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年轻黑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片琴昆。...
    茶點(diǎn)故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡氓鄙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出业舍,到底是詐尸還是另有隱情抖拦,我是刑警寧澤升酣,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站态罪,受9級特大地震影響噩茄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜复颈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一绩聘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧耗啦,春花似錦凿菩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至似将,卻和暖如春获黔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背在验。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工玷氏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人译红。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓预茄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親侦厚。 傳聞我的和親對象是個殘疾皇子耻陕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評論 2 359

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