解決真實世界問題:如何在不平衡類上使用機器學習?

原文:http://mp.weixin.qq.com/s?__biz=MzA3MzI4MjgzMw==&mid=2650718717&idx=1&sn=85038d7c906c135120a8e1a2f7e565ad
選自 SVDS
作者: TOM FAWCETT
機器之心編譯
參與:孫睿岳链、吳攀曹铃、李亞洲
本文作者 Tom Fawcett 在機器學習和數據挖掘的實踐應用上已有 20 多年的研究歷史,曾就職于 Verizon 和惠普實驗室(HP Labs)等機構丑掺,也曾在斯坦福大學語言和信息的研究中心從事過研究工作猪钮;現任 Silicon Valley Data Science 首席數據科學家兼 Machine Learning Journal 編輯品山,著有《Data Science for Business》一書。文中提到的論文可點擊「閱讀原文」下載躬贡。
如果你是機器學習課程的新手谆奥,那么你使用過的數據庫很大程度上都是非常簡單的。其中一個原因就是拂玻,當你構建分類器時,樣本類都是平衡的——也就是說宰译,每個類中的樣本數量是大致相同的檐蚜。在教學中,數據庫通常是凈化過的沿侈,這樣老師才能夠把注意力集中在教授特定算法或技巧上闯第,而不被其它問題干擾。一般情況下缀拭,你遇到的樣本類似下方的二維圖表咳短,其中的點代表樣本填帽、點的不同顏色(或形狀)代表類:

image

分類算法的目標是嘗試學習出一個能夠分辨二者的分離器(分類器)。根據不同的數學咙好、統計或幾何假設篡腌,達成這一目標的方法很多:

image

然而,當你開始面對真實的勾效、未加工過的數據時嘹悼,你會馬上注意到,這些數據要嘈雜且不平衡得多层宫。真實數據的散點圖看起來更像是這樣的:

image

最主要的問題是這些類是不平衡的:藍點的數量遠超紅點杨伙。

對于不平衡類的研究通常認為「不平衡」意味著少數類只占 10% 到 20% 。而在現實中萌腿,數據庫甚至能夠比上面的例子更加不平衡限匣。以下是一些例子:

  1. 每年,約 2% 的信用卡賬戶是偽造的 [1]毁菱。(多數的欺詐檢測領域是極其不平衡的)

  2. 針對某一病征的醫(yī)學篩查通常涵蓋了許多沒有此病征的人米死,以檢查出少數患者(例:美國的 HIV 感染率約為 0.4%)

  3. 每年,硬盤驅動器故障的發(fā)生率約為 1%

  4. 在線廣告的轉化率在 10^-3 到 10^-6 的范圍區(qū)間內

  5. 工廠的產品缺陷率一般在 0.1% 左右

以上的許多領域都是不平衡的鼎俘,因為它們是被我稱為「大海撈針問題」的那一類哲身。在這種情況中,機器學習分類器要從龐大的負面(不相關)樣本中贸伐,尋找少量的正面(相關的勘天、值得注意的)樣本。

當你遇到類似的問題時捉邢,使用傳統的算法將會面臨困難脯丝。傳統的算法通常是偏向數量占優(yōu)的類,因為它們的損失函數會試圖最優(yōu)化相關數量伏伐,例如錯誤率宠进,而沒有將數據分布納入考慮范圍內。[2]在最壞的情況中藐翎,少數類樣本會被視為多數類的異常點而被忽略材蹬,然后學習算法將生成一個簡單的分類器,并將所有樣本分類到多數類下吝镣。

這看起來像是一種不正常的行為堤器,但它真的不是。事實上末贾,如果你的目標是最大化精確度(也可以說闸溃,最小化錯誤率),這是一個完全可以接受的解決方案。但是辉川,如果我們假設那極其少量的類的樣本更需要被分類表蝙,那么我們不得不更將謹慎,并采取更復雜的方法來解決問題乓旗。

如果你正在被這樣的問題困擾府蛇,并希望得到實用的解決建議,往下看寸齐。

注:這篇博文的重點是提供解決此類問題的實用建議欲诺。然而,這并不是一個手把手教你的代碼教程渺鹦。我的 Jupyter Notebooks (https://silicon-valley-data-science.github.io/learning-from-imbalanced-classes/ImbalancedClasses.html )中有我測試這些想法的記錄扰法,但在這篇文章中,我將只解釋一些基本的想法和原則毅厚。
處理不平衡的數據

從不平衡數據中學習塞颁,是一項已被研究了 20 年之久的問題。它曾是許多論文吸耿、研討會祠锣、特別議程的主題(一項最近的調查就有大約 220 個引用)。人們嘗試了許多方法咽安,但結果各不相同伴网,所以至今沒有得到明晰的答案。當數據科學家們第一次遇到這個問題妆棒,他們往往會問:「如果我的數據是不平衡的澡腾,我該怎么做?」而這一問題是沒有固定答案的糕珊,就像你問「哪個學習算法是最好的」一樣:答案取決于數據动分。

由此,我列出了一些方法的大致總結红选,它們是以困難程度排序的:

  • 什么也不做澜公。有時好運就這樣降臨在你的頭上:你什么都不需要做。你可以使用所謂的自然(或分層)分布來進行訓練喇肋,有時不需任何修改就能正常運行坟乾。

  • 通過某些方法使得數據更加平衡:

  • 對少數類進行過采樣

  • 對多數類進行欠采樣

  • 合成新的少數類

  • 舍棄所有少數類,切換成一個異常檢測框架蝶防。

  • 在算法層面之上(或之后):

  • 調整類的權重(錯誤分類成本)

  • 調整決策閾值

  • 使已有的算法對少數類更加敏感

  • 構造一個在不平衡數據上表現更好的全新算法糊渊。

題外話:評估方面的可做與不可做

首先我要小小地跑題一下。在討論如何用不平衡數據訓練分類器之前慧脱,我們必須先探討一下如何正確評估。這是最重要的一點贺喝,因為如果你不知道該如何正確度量你的成果菱鸥,你也就不能取得進展宗兼。

  • 不要使用準確度(錯誤率)來評估你的分類器!因為這方面存在兩個顯著的問題氮采。準確度使用天真的 0.50 的閾值來確定所屬的類別殷绍,而當類不平衡時,這常常會出錯鹊漠。第二主到,分類準確度基于對錯誤的簡單計數,而你應該對此有更深入的了解躯概。你應該知道哪些類在哪些地方(得分的頂端登钥、底端還是整體)出現了混淆?如果你對這些概念還不太了解娶靡,推薦閱讀:http://www.svds.com/classifiers2/ 牧牢。你應該使用一個 ROC 曲線、準確度召回曲線(precision-recall curve)姿锭、Lift 曲線或利潤(收益)曲線(profit (gain) curve)對分類器的表現進行可視化塔鳍。
image

ROC 曲線

image

Precision-recall curve

  • 不要在你的分類器中使用困難的分類(標簽)(使用 score[3]或 predict)。而是應該使用 proba 或 predict_proba 進行概率估計呻此。

  • 當你得到概率估計之后轮纫,不要盲目地使用 0.50 的決策閾值來區(qū)分類別。應該再檢查表現曲線之后再自己決定使用哪個閾值(下一節(jié)會談到更多)焚鲜。因為研究者曾天真地使用了 0.5 作為截止線掌唾,所以早期的一些論文中出現了很多錯誤。

  • 不管你是用什么做訓練的恃泪,你總是應該在你的分類器將要運行其上的自然的(分層的)分布上進行測試郑兴。參考:sklearn.cross_validation.StratifiedKFold

  • 不使用概率估計你也可能能夠成功,但如果你需要它們贝乎,要使用校準情连。(參考:sklearn.calibration.CalibratedClassifierCV)

上面的兩幅二維圖總是比單純的數字所包含的信息更豐富,如果你需要單純的數字指標览效,下面這幾個可以很好地對應準確度:

  1. ROC 曲線下的面積(AUC)是一個很好的一般統計却舀。它等于一個隨機的正面樣本將得到比一個隨機的負面樣本更高排名的概率。

  2. F1 分數是(F1 Score)是準確度(precision)和召回率(recall)的調和平均值锤灿。它常被用于尋找聚合度量(aggregate measure)時的文本處理中挽拔。

  3. Cohen’s Kappa 系數是一種評估統計,其考慮了偶然預期的協議的多少但校。

過采樣(oversampling)和欠采樣(undersampling)

最簡單的方法只需要對處理步驟進行一點點修改螃诅,并簡單地涉及到調整樣本集直到它們達到平衡。過采樣會隨機復制少數樣例以增大它們的規(guī)模。欠采樣則隨機地少采樣主要的類术裸。一些數據科學家(天真地)認為過采樣更好倘是,因為其會得到更多的數據,而欠采樣會將數據丟掉袭艺。但請記住復制數據不是沒有后果的——因為其會得到復制出來的數據搀崭,它就會使變量的方差表面上比實際上更小。而過采樣的好處是它也會復制誤差的數量:如果一個分類器在原始的少數類數據集上做出了一個錯誤的負面錯誤猾编,那么將該數據集復制五次之后瘤睹,該分類器就會在新的數據集上出現六個錯誤。相對地答倡,欠采樣會讓獨立變量(independent variable)的方差看起來比其實際的方差更高轰传。

因為所有這些原因,不同的機器學習文獻在過采樣苇羡、欠采樣和使用自然分布之間也做出了不同的選擇绸吸。

image

大部分機器學習軟件包都可以進行簡單的采樣調整。R 中的 unbalanced中實現了很多專用于不平衡數據集的采樣技術设江,scikit-learn.cross_validation 也有一些基本的采樣算法锦茁。

Wallace 等人的貝葉斯論證

Wallace、Small叉存、Brodley 和 Trikalinos 的論文《Class Imbalance, Redux》中提出的關于類不平衡(class imbalance)的理論論證和使用建議可能是這方面做得最好的[4]码俩。他們支持對多數類進行欠采樣。他們的論證是數學的且徹底的歼捏,但這里我只給出他們用來證明自己觀點的一個例子稿存。

他們認為兩個類必須在一些解釋變量的一些分布的尾部能明顯區(qū)分開。假設你有兩個帶有單個獨立變量 x 的類瞳秽。其中每個類都是由標準差為 1 的高斯分布生成的瓣履。類 1 的均值是 1,類 2 的均值是 2. 我們任意地將類 2 定為多數類练俐。它們看起來是這樣:

image

給定一個 x 值袖迎,你會使用什么閾值來確定其來自哪一個類?很顯然這兩者之間最好的分割線是它們的中點 x=1.5腺晾,如下圖中間的豎線所示:如果新樣本 x 的值低于 1.5燕锥,那它可能屬于類 1,否則其屬于類 2悯蝉。在從樣本中進行學習時归形,我們期望在 1.5 處的分辨分割線是我們將會得到的結果;而且如果這兩個類是平衡的的話鼻由,那么這就會接近于我們應該得到的結果暇榴。x 軸上的點表示從每個分布中生成的樣本厚棵。

但我們已經說過類 1 是少數類,所以假設我們從其中得到了 10 個樣本跺撼,而從類 2 得到了 50 個樣本窟感。那我們就很可能學習到一個有所偏移的隔離線,就像這樣:

image

為了匹配少數類歉井,我們可以通過欠采樣多數類來做到更好。問題是我們所學習到的隔離線將具有較高的可變性(因為樣本更小的)哈误,如下所示(這里展示了 10 個樣本哩至,得到了 10 條豎線):

image

所以最后還要使用 bagging 來講這些分類器結合起來。整個流程看起來是這樣:

image

這種技術還沒有在 Scikit-learn 中實現蜜自,盡管已經有一個名叫 blagging.py (balanced bagging)的文件可用——實現了一個 BlaggingClassifier菩貌,可以平衡聚合(aggregation)之前的自舉樣本(bootstrapped samples)。

基于臨近的方法(Neighbor-based approaches)

過采樣和欠采樣都隨機選擇要調整的樣本的比例重荠。其它方法則仔細地檢測實例空間(instance space)和決定在它們的鄰域(neighborhood)上做什么箭阶。

比如說, Tomek link 是指相反分類中實例的配對戈鲁,這些相反分類之間是最為臨近的仇参。換句話說,它們是整體上非常接近的相反實例對婆殿。

image

Tomek 的算法可以尋找這樣的配對诈乒,并移除配對中的多數實例。其中的思想是要明確少數類和多數類之間的界線婆芦,從而使少數類的區(qū)域更明顯怕磨。下圖給出了一個簡單的 Tomek Link 移除的例子。R 包中的 unbalanced 能實現 Tomek Link 移除消约,另外還有很多專用于不平衡數據集的采樣技術也可以肠鲫。Scikit-learn 沒有做這件事的內置模塊,但也有一些獨立的包可用(如:TomekLink:https://github.com/ojtwist/TomekLink)或粮。

合成新樣本:SMOTE 及其衍生技術

研究的另一項技術不涉及對樣本的重采樣(resampling)导饲,而是合成新的樣本。這種方法最好的例子是 Chawla 的 SMOTE(Synthetic Minority Oversampling TEchnique:合成少數類過采樣技術)系統被啼。其思想是通過在已有的樣本間插值來創(chuàng)造新的少數類樣本帜消。這項技術的大概過程如下圖所示。和前面一樣浓体,假設我們有一個多數類樣本集和一個少數類樣本集:

image

SMOTE 取得了普遍的成功并衍生出了很多變體泡挺、擴展和對不同概念的學習算法的適配。SMOTE 及其變體封裝在 R 語言的 unbalanced 包和 Python 中的 UnbalancedDataset 包中命浴。

這里還要指出 SMOTE 的一個重大限制娄猫。因為它是在稀有的樣本之間插值贱除,所以它只能生成可用樣本范圍內的樣本——永遠不會生成例外的樣本。形式上媳溺,SMOTE 只能填入已有少數類樣本的凸包(Convex Hull)中月幌,但不能創(chuàng)造在少數類樣本域之外的新樣本。

調整類的權重

許多機器學習工具包都有調整類的「重要性」的方法悬蔽。比如 Scikit-learn 有許多可以使用可選的 class_weight 參數(可以設置成大于 1)的分類器扯躺。這里有一個直接從 scikit-learn 文檔中引用的例子,展示了將少數類的權重增加為 10 倍時的效果蝎困。黑色實線是使用默認設置(所有類權重相等)時得到的分割邊界录语,而虛線則是少數類(紅色)的 class_weight 參數改變到 10 倍時的效果。

image

如你所見禾乘,少數類的重要程度增加了(它的誤差被認為比其它類的誤差的成本更高)澎埠,分離的超平面(hyperplane)得到調整以降低損失。

應該指出的是調整類的重要性通常只能影響類的誤差(假陰性(False Negatives)始藕,如果少數類是陽性的話)成本蒲稳。它會調整一個分離的平面并借此降低這些誤差。當然伍派,如果該分類器在訓練集誤差上沒有錯誤江耀,那也就不需要調整,所以調整類權重可能就沒有效果拙已。

更進一步

這篇文章集中于相對簡單的决记、方便的從不平衡數據中學習分類器的方式。大部分這些都涉及到在應用標準學習算法之前或之后對數據進行調整倍踪。這里還有一些其它方法值得一提系宫。

新算法

從不平衡類進行學習一直是機器學習使用每年引入的新算法進行研究的一個領域。在總結之前建车,我先提幾個看起來有希望的算法進展扩借。

在 2014 年,Goh 和 Rudin 發(fā)表了論文《Box Drawings for Learning with Imbalanced Data》[5]缤至,該論文介紹了兩種從樣本偏斜的數據中學習的新算法潮罪。這些算法意圖圍繞少數類樣本集群創(chuàng)建「box」:

image

他們的目標是開發(fā)出對少數類的一個簡明、智能的表征领斥。他們的方程式會懲罰 box 的數量嫉到,而懲罰則被用作是一種正則化的形式。

他們介紹了兩種算法月洛,一個(Exact Boxes)使用混合整數程序提供精確但相當昂貴的解決方案何恶;另一個(Fast Boxes) 使用一個更快的集群方法生成初始 box,而后進行精煉嚼黔。實驗結果表明在大量測試數據集上细层,兩種算法都表現相當好惜辑。

前面我提到解決不平衡問題的一種方法是丟棄少數類樣本,把它當成單獨分類(或異常檢測)問題疫赎。近期的一項異常檢測技術在該問題上表現驚人盛撑。Liu、Ting 和 Zhou 介紹了一項名為 Isolation Forests 的技術捧搞,識別數據中異常的方式是通過學習隨機森林抵卫,然后測量將每個特定數據點孤立所需的決策分類的平均值。結果得到的值能被用于計算每個數據點的異常得分实牡,這個得分可被解釋為樣本所屬少數類的似然度陌僵。的確,作者們使用高度不平衡的數據測試了他們的系統创坞,并報告出了很好的結果。由 Bandaragoda受葛、Ting题涨、Albrecht、Liu 和 Wells 繼而完成的一篇論文介紹的 Nearest Neighbor Ensembles 是類似的思路总滩,而且能夠解決 Isolation Forests 的一些短板纲堵。

購買或者創(chuàng)造更多數據

本文的最后一部分的重點是在默認給定不平衡數據并且需要解決該不平衡的情況下的不平衡類的問題。在一些情況下闰渔,比如 Kaggle 競賽席函,你會被給定固定的數據集,不能再要更多的數據冈涧。

但你可能面臨一個相關的茂附、更難的問題:你在少數類數據上沒有足夠的樣本。上面的介紹的技術沒有一個可行督弓,你該怎么做营曼?

現實中,你可以購買或者創(chuàng)建一些領域中的少數類的樣本愚隧。這是機器學習領域也正在研究的一個領域蒂阱。如果少數類數據需要人們進行可靠的標記,一個常用方法是通過 Mechanical Turk 這樣的服務眾包出去狂塘。人類標記的可靠性可能是一個問題录煤,但也有將人類標記與可靠性優(yōu)化結合起來的研究。最后荞胡,Claudia Perlich 在她的演講(All The Data and Still Not Enough)中給出了一些例子:如何通過使用替代變量或問題(surrogate variable or problems)修正數據缺乏或者沒有數據的問題妈踊,或者如何使用代理和隱變量使看起來不可能的問題變得可能。與之相關的還有遷移學習(transfer learning)的方法:將在一個問題上學習到的結果遷移到另一個帶有稀少樣本的問題上硝训,參看論文《Machine learning for targeted display advertising: Transfer learning in action》响委。

資源和拓展閱讀

這里有數個說明不平衡學習不同方面的 Jupyter notebooks 可用:

一個說明高斯采樣的(sampled Gaussians)的 notebook 是在 Gaussians.ipynb:https://github.com/silicon-valley-data-science/learning-from-imbalanced-classes/blob/master/Gaussians.ipynb

一個 Wallace 方法的簡單實現 blagging.pyhttps://github.com/silicon-valley-data-science/learning-from-imbalanced-classes/blob/master/blagging.py新思。這是 sklearn 現有的 bagging 實現的簡單交叉,特別是./sklearn/ensemble/bagging.py.

使用這一方法的一個 notebook 在 ImbalancedClasses.ipynb (https://github.com/silicon-valley-data-science/learning-from-imbalanced-classes/blob/master/ImbalancedClasses.ipynb)是開放的赘风。它可裝載于數個領域夹囚,并且在不同分布下與其他方法進行了 blagging 對比。

在 MATLAB 上 Box Drawings 的源代碼:http://web.mit.edu/rudin/www/code/BoxDrawingsCode.zip

基于 R 語言的 Isolation Forests 源代碼:https://sourceforge.net/projects/iforest/

參考文獻:

  1. Natalie Hockham makes this point in her talk Machine learning with imbalanced data sets, which focuses on imbalance in the context of credit card fraud detection.

  2. By definition there are fewer instances of the rare class, but the problem comes about because the cost of missing them (a false negative) is much higher.

  3. The details in courier are specific to Python’s Scikit-learn.

  4. “Class Imbalance, Redux”. Wallace, Small, Brodley and Trikalinos. IEEE Conf on Data Mining. 2011.

  5. Box Drawings for Learning with Imbalanced Data.” Siong Thye Goh and Cynthia Rudin. KDD-2014, August 24–27, 2014, New York, NY, USA.

  6. “Isolation-Based Anomaly Detection”. Liu, Ting and Zhou. ACM Transactions on Knowledge Discovery from Data, Vol. 6, No. 1. 2012.

  7. “Efficient Anomaly Detection by Isolation Using Nearest Neighbour Ensemble.” Bandaragoda, Ting, Albrecht, Liu and Wells. ICDM-2014

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末邀窃,一起剝皮案震驚了整個濱河市荸哟,隨后出現的幾起案子,更是在濱河造成了極大的恐慌瞬捕,老刑警劉巖鞍历,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異肪虎,居然都是意外死亡劣砍,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門扇救,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刑枝,“玉大人,你說我怎么就攤上這事迅腔∽俺” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵沧烈,是天一觀的道長掠兄。 經常有香客問我,道長锌雀,這世上最難降的妖魔是什么蚂夕? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮汤锨,結果婚禮上双抽,老公的妹妹穿的比我還像新娘。我一直安慰自己闲礼,他們只是感情好牍汹,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著柬泽,像睡著了一般慎菲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锨并,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天露该,我揣著相機與錄音,去河邊找鬼第煮。 笑死解幼,一個胖子當著我的面吹牛抑党,可吹牛的內容都是我干的。 我是一名探鬼主播撵摆,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼拗胜,長吁一口氣:“原來是場噩夢啊……” “哼悴能!你這毒婦竟也來了?” 一聲冷哼從身側響起挖胃,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤爬立,失蹤者是張志新(化名)和其女友劉穎纳胧,沒想到半個月后掐暮,有當地人在樹林里發(fā)現了一具尸體涂佃,經...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年灵莲,在試婚紗的時候發(fā)現自己被綠了雕凹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡政冻,死狀恐怖请琳,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情赠幕,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布询筏,位于F島的核電站榕堰,受9級特大地震影響,放射性物質發(fā)生泄漏嫌套。R本人自食惡果不足惜逆屡,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望踱讨。 院中可真熱鬧魏蔗,春花似錦、人聲如沸痹筛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽帚稠。三九已至谣旁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間滋早,已是汗流浹背榄审。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留杆麸,地道東北人搁进。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓浪感,卻偏偏與公主長得像,于是被迫代替她去往敵國和親饼问。 傳聞我的和親對象是個殘疾皇子影兽,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內容