手把手教你用python开始第一个机器学习项目

想用python做机器学习吗,是不是在为从哪开始挠头?
这里我假定你是新手,这篇文章里咱们一起用Python完成第一个机器学习项目。
我会手把手教你以下内容:

  1. 下载python,numpy,SciPy之类软件并安装,这些是python里机器学习方面最有用的软件包。
  2. 加载一个数据集,通过统计摘要(statistical summaries)和数据可视化来了解数据集的结构。
  3. 创建6个机器学习模型,选择这里边最好的,然后介绍通过何种方法来确定选出来的模型预测时有稳定的准确率。

如果你是机器学习的初学者,并且你下定决心用python作为开始机器学习的语言的话,这篇文章应该会比较适合你。


28496101-b5a8d42c-6f9d-11e7-8bab-4bce2864d63f.png

Your First Machine Learning Project in Python Step-By-Step
Photo by cosmoflash, some rights reserved.

刚开始的时候,Python看起来有点吓人

Python是一种很流行,很强大的解释型语言。跟R不一样,对于研究,开发以及完成生产系统来说,python是一个完整的开发语言,一个完整的平台。

Python中也有许多可供选择的模块和库,对于上面说的研究,开发和完成生产系统提供了多种实现的路径。给人的感觉是python的前景势不可挡。

再次重申,用Python学习机器学习的最好方法是完成一个完整的项目。

  1. 这样做强制你安装Python,并且启动python的解释器(最少会这样)。
  2. 这样做能给你一个整体审视的机会,体验如何一步步完成一个小项目。
  3. 这样做能给你自信,让你有信心启动自己的小项目。

初学者需要一个从头到尾完成的项目

相关的书和这方面的课程很多,但是往往会让人产生挫折感。它们会给你很多的问题解决方法和代码片段,但是你很难体会到它们是如何被整合到一起的。

当你把机器学习算法应用到自己的数据集的时候,你就是在进行一个完整的项目。
一个机器学习项目可能不是像下面介绍的那样一步步按顺序完成,但是机器学习项目确实有着一些众所周知的步骤:

  1. 定义问题
  2. 准备数据
  3. 评估算法
  4. 改进结果
  5. 提交结果

熟悉一个新的平台或者一个新的工具最好的方式就是从头到尾踏实的完成一个机器学习项目,实践上述的所有关键步骤。也就是说,实际动手加载数据,统计数据,评估算法,然后做一些预测。

如果你这么做了,你就有了一个模板,这个模板是可以很容易应用到一个又一个数据集上的。一旦你建立了自信,你就可以进一步完善上述关键步骤,比如说进行进一步的数据准备和结果改善。

机器学习的Hello World
使用python开始机器学习最好的小项目是鸢尾花(yuan wei hua,英文iris,别跟虹膜弄混)的分类问题。(下载链接在此)

这个项目是个好项目,最好就在于它简单易懂。

  1. 鸢尾花的属性都是数值类型,让我们很容易想到怎么来加载数据,处理数据。
  2. 这是个分类问题,让我们能实践机器学习中一种相对简单的算法——监督学习算法。
  3. 这是个多分类问题(不是只分成两类也就是不是二分类),要求有特殊的处理。
  4. 这个数据集只有4个属性,150行数据,意味着数据集极小,用内存就能轻松搞定(也便于用屏幕显示或者直接用A4纸打印)
  5. 所有的数值属性都是相同单位,相同尺度,不需要任何的缩放和转换就能直接着手开始。

python机器学习手把手教程(这回是真开始)

这一节中,我们开始介绍怎样从头到尾完成一个小项目。
说一下我们要完成的各个步骤:

  1. 安装python和Scipy平台
  2. 加载数据集
  3. 计算数据集中的各种统计量。
  4. 数据集可视化
  5. 在数据集上应用一些算法并评估
  6. 进行预测

要走完每一步,需要一些时间。

建议自己动手输入涉及到的一些命令,或者也可以用复制粘贴来加快速度,总之动手就比只看不做强。

1. 下载安装python和Scipy平台

如果你事先没有安装过标题中的工具,请安装。这里不想非常细致的介绍安装过程,这种介绍很多。如果你是开发人员,安装软件包之类对你而言都很简单。

1.1 安装SciPy库

这里预设的python版本是2.7和3.5。这两个版本以上的版本也应该完全没有问题。

需要安装的比较关键的库有5个。下面是本文中安装Python SciPy库时需要安装的内容:
 Scipy
 Numpy
 Matplotlib
 Pandas
 Sklearn

安装这些库有很多方法。我的建议是选择一种方法然后一直坚持这种方法来安装上述的所有软件包。

链接scipyinstallation page 给安装上述库提供了非常好的指导。链接里说明了在不同平台(比如Linux,mac OS X和Windows)上如何安装。如果有任何问题,那么请参考链接中的做法,数千人这么有过相同经历。

在Mac OS X中,可以用macport来安装python2.7和这些库。Macport的信息,参见其主页。
Linux里可以用安装包管理器,比如Fedora的yum来安装RPMs。
如果你用Windows,或者你也不是太确定自己的系统,我推荐安装免费软件Anaconda。里边预装了你需要的东西。

注意:这里要求你安装的scikit-learn的版本为0.18或者以上版本。

需要更多帮助?参照下述教程:

·How to Setup a Python Environment for Machine Learning and Deep Learning with Anaconda

·How to Create a Linux Virtual Machine For Machine Learning Development With Python3

1.2 启动python,检查已安装版本

安装完成后最好确认一下你的python环境是不是能正常工作。
下面是检测环境用的脚本。其中import我们用到的每个库,并输出版本号。
打开命令行,启动python解释器
Python

我推荐直接在解释器里输入下面脚本,或者自己写好版本然后在命令行运行,而不是在一个大的编辑器或者IDE里运行。尽量让事情简单化,确保注意力关注在机器学习上而不是各种工具链上。

脚本如下:

# Checkthe versions of libraries
 
#Python version
import sys
print('Python: {}'.format(sys.version))
# scipy
import scipy
print('scipy: {}'.format(scipy.__version__))
# numpy
import numpy
print('numpy: {}'.format(numpy.__version__))
#matplotlib
import matplotlib
print('matplotlib: {}'.format(matplotlib.__version__))
#pandas
import pandas
print('pandas: {}'.format(pandas.__version__))
#scikit-learn
import sklearn
print('sklearn: {}'.format(sklearn.__version__))

下面为我本地的输出:
Python: 2.7.11 (default, Mar 1 2016, 18:40:10)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2(clang-700.1.81)]
scipy: 0.17.0
numpy: 1.10.4
matplotlib: 1.5.1
pandas: 0.17.1
sklearn: 0.18.1

比较一下你的输出。理想情况下如果你的版本跟上面的相同或者更高,就没问题。这些库里的API不会频繁变更。如果你的版本比上面的略高,这个教程里的东西应该对你仍然适用。
如果出错,请想办法更正。

如果你不能够清晰的运行以上的脚本,那么你将无法完成本教程。
建议去Google搜索一下你的错误信息,或者在Stack Exchange上提问。

2. 加载数据
我们将用到鸢尾花数据集。这个数据集很著名,因为这就是我们机器学习界和统计学界的“Hello world”数据集。
数据集中包含对鸢尾花的150次观测。数据集中有4列,都是花的一些尺寸数据,单位是厘米。第5列是观测结果,也就是花的种类。所有观测到的花都属于3种鸢尾花中的一种。
可以在维基上找到更多的信息:learn more about this dataset on Wikipedia

下一步我们将从CSV文件的URL中加载鸢尾花数据。

2.1 Import库
首先import所有在本教程中将要用到的程序模块,函数和对象。

# Loadlibraries
import pandas
from pandas.tools.plottingimport scatter_matrix
import matplotlib.pyplotas plt
from sklearn import model_selection
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
from sklearn.linear_modelimport LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighborsimport KNeighborsClassifier
from sklearn.discriminant_analysisimport LinearDiscriminantAnalysis
from sklearn.naive_bayesimport GaussianNB
from sklearn.svm import SVC

每个程序都应该正常import。如果出错,你需要重新安装python+Scipy环境。
(看一下上面关于环境安装方面的一些建议)

2.2 加载数据
我们可以直接从UCI机器学习资源库(repository)加载数据。
我们用pandas来加载数据。我们之后也会用pandas来探究描述数据的各种统计值,以及进行数据可视化。

注意我们加载数据的时候给定了每列的名称。这一点有助于我们之后探究发掘数据的特点。

# Loaddataset
url ="https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
names =['sepal-length', 'sepal-width','petal-length', 'petal-width','class']
dataset =pandas.read_csv(url, names=names)

数据集应该毫无意外的被加载。
如果你的网络有问题,你可以先下载鸢尾花数据集,然后把它放在工作路径中。加载方法跟上面的一样,只不过需要把URL换成本地文件名。

3. 数据集总结
现在是时候看一下我们的数据集了。
当前步骤中我们从不同的角度观察数据。
 1.数据维度
 2.窥视数据本身
 3.所有属性的统计概要
 4.按分类变量细分数据
上边的几种方法貌似深奥,其实不必担心,每次观察数据只需要一个命令。在未来的项目中这些命令可能会被再三的使用。

3.1数据维度
我们通过查看数据集的模型特征,就能迅速知道数据集中包含了多少数据实例(也就是行)和多少属性(也就是列)。

# shape
print(dataset.shape)

看到有150个实例,5个属性:
(150, 5)

3.2 管窥数据
实际地仔细观察数据向来都是好办法。

# head
print(dataset.head(20))

可以看到数据的先头20行:
sepal-length sepal-width petal-length petal-width class
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa
5 5.4 3.9 1.7 0.4 Iris-setosa
6 4.6 3.4 1.4 0.3 Iris-setosa
7 5.0 3.4 1.5 0.2 Iris-setosa
8 4.4 2.9 1.4 0.2 Iris-setosa
9 4.9 3.1 1.5 0.1 Iris-setosa
10 5.4 3.7 1.5 0.2 Iris-setosa
11 4.8 3.4 1.6 0.2 Iris-setosa
12 4.8 3.0 1.4 0.1 Iris-setosa
13 4.3 3.0 1.1 0.1 Iris-setosa
14 5.8 4.0 1.2 0.2 Iris-setosa
15 5.7 4.4 1.5 0.4 Iris-setosa
16 5.4 3.9 1.3 0.4 Iris-setosa
17 5.1 3.5 1.4 0.3 Iris-setosa
18 5.7 3.8 1.7 0.3 Iris-setosa
19 5.1 3.8 1.5 0.3 Iris-setosa

3.3 所有属性的统计概要
现在我们可以看一下每个属性的统计概要。
这里包括总数,均值,最小值,最大值以及一些百分比。

# descriptions
print(dataset.describe())

我们能看到所有的数值量都有相同的尺度(厘米),相似的区间,也就是0~8厘米之间。
sepal-length sepal-width petal-length petal-width
count 150.000000 150.000000 150.000000 150.000000
mean 5.843333 3.054000 3.758667 1.198667
std 0.828066 0.433594 1.764420 0.763161
min 4.300000 2.000000 1.000000 0.100000
25% 5.100000 2.800000 1.600000 0.300000
50% 5.800000 3.000000 4.350000 1.300000
75% 6.400000 3.300000 5.100000 1.800000
max 7.900000 4.400000 6.900000 2.500000

3.4 类别分布
让我们观察一下属于每一类的实例(行)的个数。我们把这个个数看成一个绝对数。

# classdistribution
print(dataset.groupby('class').size())

我们能看到每个种类包含相同数目的实例(50个,或者说每个占总数的33%)
class
Iris-setosa 50
Iris-versicolor 50
Iris-virginica 50

4. 数据可视化
现在我们对数据有了一个基本认识。我们需要在此基础上用可视化的方法,进一步加深我们的认识。

我们将介绍两种绘图方法:
1.单变量绘图,用来更好的理解每个属性。
2.多变量绘图,用来更好的理解属性之间的关系。

4.1 单变量绘图
我们先了解一下单变量的绘图,也就是用每个单独变量来绘图。
如果输入变量是数值型的,我们可以绘制每一个输入变量的箱线图(box and whisker plots)。

# boxand whisker plots
dataset.plot(kind='box',subplots=True,layout=(2,2), sharex=False, sharey=False)
plt.show()

这能让我们更清晰的了解输入属性的分布情况。


image.png

我们也可以为每个输入变量创建柱状图来了解它的分布。

#histograms
dataset.hist()
plt.show()

看起来输入变量中有两个可能符合高斯分布。这个现象值得注意,我们可以使用基于这个假设的算法。


image.png

4.2 多变量绘图
现在我们看一下变量之间的相互关系。
首先,我们看一下所有属性两两一组互相对比的散点图。这种图有助于我们定位输入变量间的结构性关系。

#scatter plot matrix
scatter_matrix(dataset)
plt.show()

注意下图中某些属性两两比对时延对角线出现的分组现象。这其实表明高度的相关性和可预测关系。


image.png

5. 算法评估
现在是时候为数据建模,然后评估这些模型在未见数据的准确率了。未见数据指模型从来没有见过的数据(译者注:将来的数据,例如不在训练数据,也不在测试数据中的数据)。
这个步骤里边我们介绍的内容如下:
 1.分割验证数据集
 2.设置测试工具,使用十折交叉验证(10-fold cross validation)。
 3.建立6(5)个不同模型,用花的各种尺寸来预测花的种类。
 4.选择最好的模型。

5.1 创建验证数据集
我们需要知道我们创建的模型好还是不好,好在哪里。

之后我们会通过统计学方法来估计我们的模型在未见数据上的准确率。我们也想,通过衡量最佳模型在实际未见数据上的准确率,得到最佳模型在未见数据上的更加具体的准确率估计。

这样的话,我们需要预先保留一些数据,这些数据对算法是不可见的。我们会用这些保留数据来帮助我们了解最佳模型在实际做预测时有多精准。

我们把加载的数据集分成两部分,其中的80%分为一部分,我们用它来训练我们的模型。其中的20%我们保留,作为验证数据集。

#Split-out validation dataset
array =dataset.values
X =array[:,0:4]
Y =array[:,4]
validation_size = 0.20
seed =7
X_train, X_validation, Y_train,Y_validation = model_selection.train_test_split(X, Y, test_size=validation_size, random_state=seed)

现在有了训练数据集:X_train,Y_train
也有了之后要用的验证数据集:X_validation,Y_validation

5.2 测试工具集
我们用十折交叉验证来估计准确率。
我们的具体做法是将数据集分成10份,9份用于训练,1份用于测试。交叉验证过程涵盖训练部分测试部分分割的所有组合(也就是每一份都要做一次测试集)。

# Testoptions and evaluation metric
seed =7
scoring= 'accuracy'

我们用了标准“准确率”来衡量我们的模型。准确率=预测正确实例数/数据集中实例总数100(例如95%的准确率)。当我们运行和评估每个模型时,我们会用到上述代码中的scoring变量。

5.3 建立模型
我们不知道对于我们的问题来说哪种算法比较好,或者要用哪种配置。我们从绘图中感觉有些分类在某些维度上线性可分(看我说得多么不确定),我们期待我们的观察具有普遍性。
下面我们评估6种不同算法:
 逻辑回归(LR)
 线性判别分析(LDA)
 K近邻法(KNN)
 分类回归树(CART)
 高斯朴素贝叶斯分类器(NB)
 支持向量机(SVM)
上面混合了简单线性算法(LR和LDA),和非线性算法(KNN,CART,NB和SVM)。我们每次运行之前重设随机数种子,保证每个算法的评估是针对相同数据分割来进行。从而确保结果可以直接比较。

让我们先建立上述6种模型,然后评估:

# SpotCheck Algorithms
models =[]
models.append(('LR', LogisticRegression()))
models.append(('LDA', LinearDiscriminantAnalysis()))
models.append(('KNN', KNeighborsClassifier()))
models.append(('CART', DecisionTreeClassifier()))
models.append(('NB', GaussianNB()))
models.append(('SVM', SVC()))
#evaluate each model in turn
results =[]
names =[]
for name, model inmodels:
    kfold= model_selection.KFold(n_splits=10, random_state=seed)
    cv_results= model_selection.cross_val_score(model,X_train, Y_train, cv=kfold, scoring=scoring)
    results.append(cv_results)
    names.append(name)
    msg= "%s: %f (%f)" % (name, cv_results.mean(),cv_results.std())
    print(msg)

5.4 选择最佳模型
我们现在有了6个模型和每个模型的准确率评估。我们需要对这些模型相互之间进行比较,选择一个最精确的模型。

运行上述的示例代码,我们得到下面的原始结果:
LR: 0.966667(0.040825)
LDA: 0.975000(0.038188)
KNN: 0.983333(0.033333)
CART: 0.975000(0.038188)
NB: 0.975000(0.053359)
SVM: 0.981667 (0.025000)
我们可以看到看起来KNN估计准确率的分值最高。

我们也能绘出模型评估结果的图形,可以比较每种模型的对于分类数据的覆盖率和平均准确度。每个算法准确度度量的结果都有多个,因为我们每种算法都评估了10次(十折交叉验证)。

#Compare Algorithms
fig =plt.figure()
fig.suptitle('Algorithm Comparison')
ax =fig.add_subplot(111)
plt.boxplot(results)
ax.set_xticklabels(names)
plt.show()

下图中可以看到箱线图都被挤压在范围的顶部,很多样本达到100%的准确率。


image.png

6. 预测
经过测试,KNN准确率最好。现在我们要看一下模型在我们的验证集上的精度。验证集上的准确率是独立检测最佳模型的准确率的最终手段。保留一个验证集的价值在于防止你在训练的时候掉到坑里,比如训练集上出现过拟合或者数据遗漏。两者都会造成过度乐观的结果。

我们在验证集上直接运行KNN模型,汇总结果作为最终准确率的评分,得到一个混淆矩阵和分类报告。

# Makepredictions on validation dataset
knn =KNeighborsClassifier()
knn.fit(X_train,Y_train)
predictions = knn.predict(X_validation)
print(accuracy_score(Y_validation, predictions))
print(confusion_matrix(Y_validation, predictions))
print(classification_report(Y_validation, predictions))

我们可以看到准确率是0.9或者90%。混淆矩阵显示有3个预测错误。最终,分类报告按精度,召回率,F1分值(f1-score),支撑集件数(support)等指标给出了每种分类的明细(前提是验证集比较小)。

0.9

[[ 7 0 0]
[ 0 11 1]
[ 0 2 9]]

     precision recall f1-score support
Iris-setosa 1.00 1.00 1.00 7
Iris-versicolor 0.85 0.92 0.88 12
Iris-virginica 0.90 0.82 0.86 11

avg / total 0.90 0.90 0.90 30

你也可以用Python进行机器学习
上述的教程会花费你5~10分钟,最多。

你不需要理解上面提到的每个细节(最少现在不需要)。你的目标应该是通过教程,从头到尾完完整整的执行一遍,得到一个结果。不需要第一遍就理解全部细微部分。你可以记录你的疑问。多多使用python的帮助文档的语法:help(“函数名”)来学习你正在使用的所有函数。

在这里你不需要知道算法是怎样一个内部机制。重要的是了解机器学习算法的限制和怎么样配置一个机器学习算法。关于算法的学习请在之后进行。你可能需要通过很长的时间慢慢积累你对算法的认知。今天,只要开始熟悉这样一个开发平台就好。

你不需要是一个python程序员。即使你是第一次接触python,也应该可以轻松看懂Python代码,因为Python语言的语法符合语言直觉。跟其他语言一样,关注函数调用(如function())和赋值(如a=”b”)。这样你就可以理解大部分了。如果你是一个软件开发人员,你肯定知道怎么样快速掌握一个语言的基本语法。先开始,之后再追究细节。

你不需要是一个机器学习专家。你可以随后再学习掌握各种算法的优劣和限制,有很多的网络内容可供阅读,来逐渐理解机器学习项目各个步骤以及交叉验证来评估准确率的重要性。

机器学习项目中的其他步骤有什么呢?我们并没有涵盖所有机器学习项目的步骤,因为这是我们的第一个项目,我们需要将注意力放到关键步骤上面,也就是加载数据,查看数据,评估算法和预测。后续的教程中我们再来看看其他的数据准备和结果改进之类的任务。

总结
在本文中介绍了怎样在python中一步一步完成一个完整的机器学习项目。从头到尾完成一个小项目,包括加载数据到进行预测,是熟悉新平台最好的方式。

下一步要做的:
你动手完成上述教程了吗?
1.完成上述教程
2.列出你的所有问题
3.再次再三搜索答案
4.请记住,Python里可以用help(“function”)来查看每个函数的帮助信息

原文链接:
http://machinelearningmastery.com/machine-learning-in-python-step-by-step/
作者:
Jason Brownlee

image.png

【PanoTech】识别上图二维码关注我们

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容