98-非監(jiān)督學習之k-means聚類

> library(pacman)
> p_load(dplyr, mclust, GGally)

聚類:在數(shù)據(jù)中識別相似行的技術纽谒。
常見聚類技術:k-means, DBSCAN, OPTICS

k-means 是一種基于劃分的聚類算法丛版,它以 k 為參數(shù)穿剖,把 n 個數(shù)據(jù)對象分成 k 個簇超埋,使簇內具有較高的相似度,而簇間的相似度較低洼畅。常見算法有:Lloyd 算法吩案;MacQueen 算法和Hartigan-Wong 算法。

1帝簇、k-means聚類思想

k-means 算法是根據(jù)給定的 n 個數(shù)據(jù)對象的數(shù)據(jù)集徘郭,構建 k 個劃分聚類的方法靠益,每個劃分聚類即為一個簇。該方法將數(shù)據(jù)劃分為 n 個簇崎岂,每個簇至少有一個數(shù)據(jù)對象捆毫,每個數(shù)據(jù)對象必須屬于而且只能屬于一個簇。同時要滿足同一簇中的數(shù)據(jù)對象相似度高冲甘,不同簇中的數(shù)據(jù)對象相似度較小绩卤。聚類相似度是利用各簇中對象的均值來進行計算的。

k-means 算法的處理流程如下:
首先江醇,隨機地選擇 k 個數(shù)據(jù)對象濒憋,每個數(shù)據(jù)對象代表一個簇中心,即選擇 k 個初始中心陶夜;對剩余的每個對象凛驮,根據(jù)其與各簇中心的相似度(距離),將它賦給與其最相似的簇中心對應的簇条辟;
然后重新計算每個簇中所有對象的平均值黔夭,作為新的簇中心;
不斷重復以上過程羽嫡,直到準則函數(shù)(通常采用均方差作為準則函數(shù)本姥,即最小化每個點到最近簇中心的距離的平方和)收斂,也就是簇中心不發(fā)生明顯的變化杭棵。

2婚惫、算法步驟

2.1 Lloyd算法步驟

1.選擇k個中心。
2.在特征空間中隨機初始化k個中心魂爪。
3.對每行計算其和每個中心之間的距離先舷。
4.將各行分配到最近中心點的簇中。
5.將每個中心移動到其所在簇的平均值處滓侍。
6.重復步驟3和4蒋川,直到沒有行改變簇或達到最大迭代次數(shù)。

2.2 MacQueen算法步驟

Lloyd的算法:批處理或離線算法撩笆。
MacQueen的算法:增量或在線算法尔破。

1.選擇k個中心。
2.在特征空間中隨機初始化k個中心浇衬。
3.將各行按距離分配到最近中心點的簇中。
4.將每個中心移動到其所在簇的平均值處餐济。
5.對每行耘擂,與各中心計算距離并分配到最近中心點的簇中。若有行換簇絮姆,則更新中心醉冤。
6.每行處理完后秩霍,更新所有中心點。
7.若沒有行改變簇則停止蚁阳,否則重復步驟5铃绒。

2.3 Hartigan-Wong算法步驟

1.選擇k個中心。
2.在特征空間中隨機初始化k個中心螺捐。
3.將各行按距離分配到最近中心點的簇中颠悬。
4.將每個中心移動到其所在簇的平均值處。
5.對每行定血,計算其在每簇時的SSE赔癌,并分到SSE最小的簇中。若有行換簇澜沟,則更新中心灾票。
6.若沒有行改變簇則停止,否則重復步驟5茫虽。

2.4 距離計算

R中dist()函數(shù)計算距離刊苍。
dist(x, method = "euclidean", diag = FALSE, upper = FALSE, p = 2)
x:數(shù)據(jù)矩陣或數(shù)據(jù)框
method:距離計算方法,包括"euclidean"-歐氏距離濒析,"maximum"-切比雪夫距離正什,"manhattan"-曼哈頓距離(絕對值距離,馬氏距離)悼枢,"canberra"-蘭氏距離埠忘,"binary"-定性變量距離,"minkowski"-明考斯基距離馒索。
diag,upper:輸出距離矩陣的式樣莹妒,默認只輸出下三角矩陣。
p:明氏距離的冪次绰上。

在計算距離之前旨怠,一般需要將數(shù)據(jù)中心化或標準化處理,消除不同量綱或量級的影響蜈块。

3 k-means聚類實例

> data(GvHD, package = "mclust")
> gvhd <- as_tibble(GvHD.control)
> str(gvhd)
## tibble [6,809 × 4] (S3: tbl_df/tbl/data.frame)
##  $ CD4 : num [1:6809] 199 294 85 19 35 376 97 200 422 391 ...
##  $ CD8b: num [1:6809] 420 311 79 1 29 346 329 342 433 390 ...
##  $ CD3 : num [1:6809] 132 241 14 141 6 138 527 145 163 147 ...
##  $ CD8 : num [1:6809] 226 164 218 130 135 176 406 189 47 190 ...
> DataExplorer::profile_missing(gvhd)
## # A tibble: 4 x 3
##   feature num_missing pct_missing
##   <fct>         <int>       <dbl>
## 1 CD4               0           0
## 2 CD8b              0           0
## 3 CD3               0           0
## 4 CD8               0           0

探索性可視化:

> ggpairs(gvhd, upper = list(continuous = "density"),
+         lower = list(continuous = wrap("points", size = 0.4)),
+         diag = list(continuous = "densityDiag"))
探索性可視化

從圖中隱約可以看到分成3類較為合適鉴腻。
k-means聚類:

> # 標準化
> gvhd.scale <- scale(gvhd)
> # 分別使用三種算法聚類
> # centers:聚類的個數(shù)或初識類重心
> # iter.max:最大迭代次數(shù),默認10
> # nstart:隨機集合個數(shù)百揭,默認1
> # algorithm:動態(tài)聚類算法爽哎,默認為Hartigan-Wong
> gvhd.lloyd <- kmeans(gvhd.scale, centers = 3, algorithm = "Lloyd")
> gvhd.hartiganwong <- kmeans(gvhd.scale, centers = 3, algorithm = "Hartigan-Wong")
> gvhd.macqueen <- kmeans(gvhd.scale, centers = 3, algorithm = "MacQueen")

結果解讀:

> gvhd.lloyd
## K-means clustering with 3 clusters of sizes 4818, 433, 1558
## 
## Cluster means:
##          CD4       CD8b         CD3         CD8
## 1  0.4891758  0.3349026 -0.02166058 -0.24424752
## 2 -0.9043872  0.5886317  1.68486535  2.77669629
## 3 -1.2613925 -1.1992544 -0.40127474 -0.01638313
## 
## Clustering vector:
##    [1] 1 1 3 3 3 1 2 1 1 1 1 1 1 3 3 2 3 3 3 3 1 3 1 1 1 1 3 1 1 1 1 1 1 1 1 3 1 2 3 1 1 2 1 3 1 3 1 1 2 1 1 3
##   [53] 1 1 1 1 3 1 1 1 3 1 3 1 3 1 1 1 1 1 1 1 2 1 3 1 1 1 1 1 1 1 3 3 2 1 1 3 1 3 1 1 1 2 1 1 1 1 1 3 1 1 1 3
##  [105] 3 1 1 1 1 1 1 1 1 1 1 3 1 1 1 3 3 1 1 3 1 1 3 1 1 1 1 1 1 1 1 1 3 1 3 1 1 1 3 1 1 3 1 3 1 1 1 1 1 3 3 3
##  [157] 1 3 1 1 3 2 1 1 2 1 1 1 3 1 1 1 1 1 1 1 3 1 3 3 1 1 3 1 1 3 1 1 1 3 3 3 1 3 1 1 2 1 1 1 1 1 1 1 3 3 3 1
##  [209] 1 3 1 1 3 3 3 1 1 3 3 1 1 3 1 1 3 1 1 3 1 1 3 1 1 1 3 3 1 1 1 3 1 1 1 1 2 1 1 1 1 1 3 1 3 3 3 3 3 1 2 3
##  [261] 1 3 3 1 2 3 1 3 1 1 1 3 1 3 1 1 1 3 3 1 1 1 1 3 3 1 1 1 3 2 3 1 1 1 3 1 1 3 1 3 1 2 1 3 3 1 1 1 3 3 2 1
##  [313] 1 3 3 3 1 1 1 1 1 3 1 1 1 1 1 1 3 1 1 1 1 1 1 1 3 1 1 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1
##  [365] 3 1 1 1 1 2 1 3 1 1 1 1 3 3 1 3 3 1 3 1 1 1 1 1 3 3 3 1 1 1 1 1 1 3 1 1 2 3 1 3 1 1 3 3 1 1 1 3 1 1 1 1
##  [417] 1 1 1 1 3 1 1 1 1 1 1 1 3 1 3 1 1 1 1 3 1 1 1 1 1 1 1 2 3 1 2 2 1 2 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 3 1
##  [469] 1 1 3 1 3 3 1 1 1 1 1 1 1 1 1 3 1 3 1 1 3 2 1 1 1 1 1 1 1 2 1 1 1 1 1 1 3 3 1 1 3 1 3 1 1 1 1 1 2 1 1 1
##  [521] 1 1 1 1 1 3 3 3 2 1 1 1 1 1 1 1 1 2 1 1 3 3 3 1 3 3 1 1 1 1 1 1 3 1 1 1 1 3 1 1 2 1 1 3 3 1 3 1 2 3 1 1
##  [573] 2 1 3 2 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 3 1 3 1 1 1 3 2 3 1 1 1 1 1 2 1 1
##  [625] 1 1 1 3 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 3 1 1 1 1 1 1 1 3 1 1 1 1 1 3 1 1 3 3 3 1 3 1 1 1 3 1 2
##  [677] 1 3 1 3 1 2 3 3 3 1 2 3 1 1 1 1 3 3 1 3 1 3 1 1 3 1 3 2 1 1 3 1 2 1 1 1 1 1 3 2 1 3 1 1 1 1 1 1 1 3 1 1
##  [729] 1 1 1 1 3 1 2 1 1 1 3 3 1 1 3 3 1 3 1 1 1 1 1 1 2 3 1 1 1 1 1 1 1 1 3 3 2 1 3 1 1 1 1 1 3 1 3 1 1 1 1 1
##  [781] 1 2 1 3 1 1 1 1 1 1 1 3 1 1 3 1 1 1 3 3 1 1 3 1 1 1 1 1 1 1 1 1 1 3 3 1 1 3 2 3 1 1 3 1 1 1 3 3 1 1 1 1
##  [833] 1 1 1 1 3 1 3 1 3 2 1 1 1 3 3 1 1 1 1 3 1 3 1 3 1 1 3 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 3 3 1
##  [885] 3 2 3 1 1 1 1 3 3 1 1 1 3 3 3 1 1 1 3 1 1 3 3 3 3 1 2 1 1 1 3 1 1 2 1 1 1 3 1 1 3 1 1 3 1 2 3 2 1 1 1 2
##  [937] 1 1 1 1 1 1 2 1 3 1 1 1 1 1 1 1 1 1 3 1 1 3 1 1 1 1 2 1 1 1 1 1 1 1 1 3 1 3 1 1 1 1 1 1 1 2 1 1 1 1 1 1
##  [989] 1 1 3 1 2 1 1 1 1 1 1 1
##  [ reached getOption("max.print") -- omitted 5809 entries ]
## 
## Within cluster sum of squares by cluster:
## [1] 11329.51  1451.36  2425.35
##  (between_SS / total_SS =  44.2 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss" "betweenss"    "size"        
## [8] "iter"         "ifault"

K-means clustering with 3 clusters of sizes 4293, 1847, 669:3個類的大小分別為4293, 1847, 669。
Cluster means:中心點的值器一。
Clustering vector:分組索引课锌。
Within cluster sum of squares by cluster:組內平方和。
(between_SS / total_SS = 50.0 %):組間平方和/總平方和,用于衡量點聚集程度渺贤。
Available components:聚類后對象(gvhd.lloyd)的屬性雏胃。比如要查看每個類的大小:

> gvhd.lloyd$size
## [1] 4818  433 1558

對象屬性解讀:
cluster志鞍,每個點的分組
centers瞭亮,聚類的中心點坐標
totss,總平方和
withinss固棚,組內平方和
tot.withinss统翩,組內總平方和,sum(withinss)
betweenss玻孟,組間平方和唆缴,totss – tot.withinss
size,每個組中的數(shù)據(jù)點數(shù)量
iter黍翎,迭代次數(shù)
ifault面徽,可能有問題的指標

對比三種算法的聚類結果,分別對比組內平方總和和組間平方和匣掸。

> tibble(algorithm = c("Lloyd", "Hartigan-Wong","MacQueen"),
+        withinss = c(gvhd.lloyd$totss, gvhd.hartiganwong$totss, 
+                     gvhd.macqueen$totss),
+        betweenss = c(gvhd.lloyd$betweenss, gvhd.hartiganwong$betweenss,
+                      gvhd.macqueen$betweenss))
## # A tibble: 3 x 3
##   algorithm     withinss betweenss
##   <chr>            <dbl>     <dbl>
## 1 Lloyd           27232.    12026.
## 2 Hartigan-Wong   27232.    13396.
## 3 MacQueen        27232.    12258.

要讓組內平方和盡量小趟紊,組間平方和盡量大,所以"Lloyd"和"MacQueen"算法結果相差很小碰酝,都比"Hartigan-Wong"算法更差霎匈。

4 k-means聚類的優(yōu)缺點

優(yōu)點:原理簡單,收斂快送爸,效果優(yōu)铛嘱,參數(shù)少(只有K)。
缺點:K不好把握袭厂;不能自然地處理分類變量墨吓;對不同尺度的數(shù)據(jù)敏感;要求隱含類方差近似相等纹磺;對異常點敏感帖烘;采用迭代只是局部最優(yōu);由于初始中心點的隨機性橄杨,每次聚類可能結果略有區(qū)別秘症;優(yōu)先尋找球形聚類,即使不符實情式矫。

5 k-means聚類mlr3實現(xiàn)

真正最新理念乡摹、最新技術、最新一代的系統(tǒng)地實現(xiàn)機器學習算法的R包采转,比Python中的 sklearn 還先進趟卸。
任務(Task):封裝了數(shù)據(jù)及額外信息,如預測目標;
學習器(Learner):封裝了多種機器學習算法锄列,能夠訓練模型并做預測,大多數(shù)學習器都有影響其操作的超參數(shù)惯悠;
度量(Measure):基于預測值與真實值的差異計算數(shù)值得分邻邮;
重抽樣(Resampling):生成一系列的訓練集和測試集。
另外克婶,實際數(shù)據(jù)的機器學習建模工作流筒严,還包括標準化、缺失值插補情萤、特征選擇等鸭蛙。

初識mlr3:http://www.reibang.com/p/7766aa7f2ef7

5.1 創(chuàng)建任務

> p_load(mlr3, mlr3cluster)
> 
> # 創(chuàng)建任務
> task <- TaskClust$new(id = "gvhd", backend = gvhd)
> 
> # 探索性可視化
> mlr3viz::autoplot(task, type = "pairs")
探索性可視化

其實也是使用GGally包畫的圖。

5.2 創(chuàng)建學習器

> # 查看支持哪些學習器
> mlr_learners$keys("clust")
## [1] "clust.agnes"       "clust.cmeans"      "clust.dbscan"      "clust.diana"       "clust.fanny"      
## [6] "clust.featureless" "clust.kmeans"      "clust.pam"         "clust.xmeans"

可以看到筋岛,支持的聚類算法有:clust.agnes, clust.cmeans, clust.dbscan, clust.diana, clust.fanny, clust.featureless, clust.kmeans, clust.pam, clust.xmeans娶视。

我們使用k-means聚類:

> learner.kmeans <- mlr_learners$get("clust.kmeans")
> # 查看信息
> print(learner.kmeans)
## <LearnerClustKMeans:clust.kmeans>
## * Model: -
## * Parameters: centers=2
## * Packages: stats, clue
## * Predict Type: partition
## * Feature types: logical, integer, numeric
## * Properties: complete, exclusive, partitional
> # 查看可以設置哪些超參數(shù)
> learner.kmeans$param_set$ids()
## [1] "centers"   "iter.max"  "algorithm" "nstart"    "trace"

跟kmeans函數(shù)的參數(shù)是一致的。
設置十折交叉驗證:

> resampling <- rsmp("cv", folds = 10L)

5.3 訓練和度量

度量函數(shù):

> # 查看有哪些度量函數(shù)
> mlr_measures$keys("clust")
## [1] "clust.ch"         "clust.db"         "clust.dunn"       "clust.silhouette"

clust.ch:Calinski-Harabasz 偽 F 統(tǒng)計量睁宰,反映聚類間方差和聚類內方差的比率肪获,CH越大代表著類自身越緊密,類與類之間越分散柒傻,即更優(yōu)的聚類結果孝赫。
clust.db:Davies-Bouldin Index(戴維森堡丁指數(shù))(分類適確性指標)(DB,DBI)红符,DB越小意味著類內距離越小青柄,同時類間距離越大。缺點:因使用歐式距離预侯,所以對于環(huán)狀分布聚類評測很差致开。
clust.dunn:Dunn Validity Index (鄧恩指數(shù))(DVI),DVI越大意味著類間距離越大雌桑,同時類內距離越小喇喉。缺點:對離散點的聚類測評很高、對環(huán)狀分布測評效果差校坑。
clust.silhouette:輪廓系數(shù)拣技,是聚類效果好壞的一種評價方式。最早由 Peter J. Rousseeuw 在 1986 提出耍目。它結合內聚度和分離度兩種因素膏斤。可以用來在相同原始數(shù)據(jù)的基礎上評價不同算法邪驮、或者算法不同運行方式對聚類結果所產生的影響莫辨。如果一個簇中的大多數(shù)樣本具有比較高的輪廓系數(shù)(接近1),則簇會有較高的總輪廓系數(shù),則整個數(shù)據(jù)集的平均輪廓系數(shù)越高沮榜,則聚類是合適的盘榨。如果許多樣本點具有低輪廓系數(shù)甚至負值(接近-1),則聚類是不合適的蟆融,聚類的超參數(shù)K可能設定得太大或者太小草巡。

> measure <- msrs(c("clust.ch", "clust.db", "clust.silhouette"))

根據(jù)不同的算法,設計訓練:

> design <- benchmark_grid(
+   task = task,
+   learner = list(
+     lrn("clust.kmeans", algorithm = "Hartigan-Wong"),
+     lrn("clust.kmeans", algorithm = "Lloyd"),
+     lrn("clust.kmeans", algorithm = "MacQueen")
+   ),
+   resampling = resampling)
> print(design)
##               task                  learner         resampling
## 1: <TaskClust[41]> <LearnerClustKMeans[31]> <ResamplingCV[19]>
## 2: <TaskClust[41]> <LearnerClustKMeans[31]> <ResamplingCV[19]>
## 3: <TaskClust[41]> <LearnerClustKMeans[31]> <ResamplingCV[19]>
> bmr <- benchmark(design = design)
> results <- bmr$aggregate(measures = measure)

結果表明Hartigan-Wong算法在該數(shù)據(jù)集上是最優(yōu)的型酥。
畫個圖看看:

> tibble(algorithm = c("Hartigan-Wong", "Lloyd", "MacQueen"),
+        # 數(shù)值同比例縮小山憨,便于畫圖
+        clust.ch = results$clust.ch / 200,
+        clust.db = results$clust.db,
+        clust.silhouette = results$clust.silhouette) %>% 
+   # 寬表變長表
+   tidyr::pivot_longer(names_to = "metric", values_to = "value", -c("algorithm")) %>% 
+   ggplot(aes(metric, value, fill = algorithm)) +
+   geom_bar(stat = "identity", position = "dodge", width = 0.5) +
+   labs(x = "", y = "", title = "不同聚類算法對比") +
+   theme_bw() +
+   theme(legend.position = "top",
+         plot.title = element_text(hjust = 0.5))
不同算法聚類結果對比

使用同樣的方法可以找到最優(yōu)的K值,然后使用最優(yōu)的參數(shù)重新建模弥喉,并對新數(shù)據(jù)進行預測郁竟。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市由境,隨后出現(xiàn)的幾起案子棚亩,更是在濱河造成了極大的恐慌,老刑警劉巖藻肄,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蔑舞,死亡現(xiàn)場離奇詭異,居然都是意外死亡嘹屯,警方通過查閱死者的電腦和手機攻询,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來州弟,“玉大人钧栖,你說我怎么就攤上這事∑畔瑁” “怎么了拯杠?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長啃奴。 經常有香客問我潭陪,道長,這世上最難降的妖魔是什么最蕾? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任依溯,我火速辦了婚禮,結果婚禮上瘟则,老公的妹妹穿的比我還像新娘黎炉。我一直安慰自己,他們只是感情好醋拧,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布慷嗜。 她就那樣靜靜地躺著淀弹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪庆械。 梳的紋絲不亂的頭發(fā)上薇溃,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音干奢,去河邊找鬼痊焊。 笑死,一個胖子當著我的面吹牛忿峻,可吹牛的內容都是我干的。 我是一名探鬼主播辕羽,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼逛尚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了刁愿?” 一聲冷哼從身側響起绰寞,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎铣口,沒想到半個月后滤钱,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡脑题,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年件缸,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叔遂。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡他炊,死狀恐怖,靈堂內的尸體忽然破棺而出已艰,到底是詐尸還是另有隱情痊末,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布哩掺,位于F島的核電站凿叠,受9級特大地震影響,放射性物質發(fā)生泄漏嚼吞。R本人自食惡果不足惜盒件,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望誊薄。 院中可真熱鬧履恩,春花似錦、人聲如沸呢蔫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至绽昏,卻和暖如春协屡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背全谤。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工肤晓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人认然。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓补憾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親卷员。 傳聞我的和親對象是個殘疾皇子盈匾,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345