让你"又爱又恨"的推荐系统--程序猿篇

推荐系统

0、又爱又恨的推荐系统

作为一名程序猿,一直对推荐系统比较感兴趣,最近看到一个用户的吐槽:


又爱又恨

推荐系统的应用场景,我相信在日常生活中大家基本都会接触到。例如,作为一个篮球爱好者,在淘宝上搜索的“kobe X 篮球鞋”,然后之后一段时间打开淘宝,首页界面可能会推荐很多与篮球鞋相关的商品,这算是一个比较正常的应用场景吧。当然还可能有其它的一些场景,例如上面用户吐槽的手机麦克风可能被监控,进而自己的喜好被平台方获取并产生推荐......

一个好的推荐系统不可避免的需要准确、尽可能详细的了解目标用户的喜好特征,有时候不经意间触碰到用户的隐私,这可能会引起用户的抵触情感。

但是,一个好的推荐系统又是被用户所推崇喜爱的,例如:网易云音乐,它的歌单推荐功能,我相信这是很多使用网易音乐的用户选择这款音乐应用的重要原因之一。

至于,如何平衡保护用户隐私与实现推荐系统功能,我认为这就需要平台方与用户有充分的交互与信任,平台有义务向用户透明应用会获取的用户信息,用户有权利保护个人不想透露的隐私信息。

毕竟,一个优秀的推荐系统是应该能够让用户与平台方实现双赢的局面。
以上,是一个程序猿的浅薄见解,还是做回老本行,介绍一下推荐系统的基础技术吧!

1、为什么需要推荐系统

如今,我们这代人正经历从信息时代(Information Technology,IT)到数据时代(Data Technology,DT)的变迁,DT时代比较明显的标志就是:信息过载

罗胖2017跨年演讲

在DT时代,充斥着海量的信息,如何从海量的信息中快捷的帮助特定用户找到感兴趣的信息呢?有两种相关的解决技术:搜索引擎与推荐系统。

搜索引擎与推荐系统有什么区别?
搜索引擎:实现人找信息,eg.百度搜索...
推荐系统:实现信息找人,eg.亚马逊的图书推荐列表...

与搜索引擎不同,推荐系统不需要用户准确地描述出自己的需求,而是根据分析历史行为建模,主动提供满足用户兴趣和需求的信息。


亚马逊商城

由此,可见推荐系统关注的是如何主动的为需求尚未明确的用户,推荐他们可能感兴趣的信息。

例如,消费者如何不经意间发现自己喜欢的商品,生产者以及平台方如何让自己的商品脱颖而出,增加销量,挖掘商品’长尾’…..推荐系统就是为了解决这些问题的。
简单来说,对于消费者而言,他们喜欢用2个小时去看一部感兴趣的电影,却不愿意花20分钟去挑选,这就是个性化推荐系统存在的意义

2、什么是推荐系统?

推荐系统通过分析、挖掘用户行为,发现用户的个性化需求与兴趣特点,将用户可能感兴趣的信息或商品推荐给用户。一个优秀的推荐系统,能够很好的串联起用户、商家以及平台方,并让三方都收益。

推荐系统

本质上来讲,推荐系统就是对所有商品针对特定用户进行按照一定策略进行排序,然后筛选出若干商品推荐给用户的过程。

2.1、传统的推荐系统方法主要有:

  1. 协同过滤推荐(Collaborative Filtering Recommendation):该方法收集分析用户历史行为、活动、偏好,计算一个用户与其他用户的相似度,利用目标用户的相似用户对商品评价的加权评价值,来预测目标用户对特定商品的喜好程度。优点是可以给用户推荐未浏览过的新产品;缺点是对于没有任何行为的新用户存在冷启动的问题,同时也存在用户与商品之间的交互数据不够多造成的稀疏问题,会导致模型难以找到相近用户。
  2. 基于内容过滤推荐[1](Content-based Filtering Recommendation):该方法利用商品的内容描述,抽象出有意义的特征,通过计算用户的兴趣和商品描述之间的相似度,来给用户做推荐。优点是简单直接,不需要依据其他用户对商品的评价,而是通过商品属性进行商品相似度度量,从而推荐给用户所感兴趣商品的相似商品;缺点是对于没有任何行为的新用户同样存在冷启动的问题。
  3. 组合推荐[2](Hybrid Recommendation):运用不同的输入和技术共同进行推荐,以弥补各自推荐技术的缺点。

推荐系统基础知识储备

3、协同过滤推荐

基于协同过滤推荐算法的思想是:通过对用户历史行为数据的挖掘发现用户的偏好,基于不同的偏好对用户进行群组划分并推荐品味相似的项。在计算推荐结果的过程中,不依赖于项的任何附加信息或者用户的任何附加信息,只与用户对项的评分有关。

数据集构成

通常有两种方法:

1、通过相似用户进行推荐。通过比较用户之间的相似性,越相似表明两者之间的品味越相近,这样的方法被称为基于用户的协同过滤算法(User-based Collaborative Filtering);

2、通过相似项进行推荐。通过比较项与项之间的相似性,为用户推荐与评价过的项的相似项,这样的方法被称为基于项的协同过滤算法(Item-based Collaborative Filtering)。

基于用户的:User-based Collaborative Filtering,为用户推荐和他兴趣相似的用户喜欢的商品。
基于项(商品)的:tem-based Collaborative Filtering,为用户推荐与他之前喜欢的商品相似度高的商品.

这个算法的核心,就是如何衡量用户与用户之间的相似度或者商品与商品之间的相似度

相似性的度量方法有很多种,比如:欧式距离、皮尔森相关系数、余弦相似度等

欧式距离是使用的比较多的相似性度量方法,其用欧式距离作为样本之间的相似性的度量,但是在欧式距离的计算中,不同特征之间的量级对欧式距离的影响比较大,但是皮尔森相关系数对量级不敏感。

余弦相似度是文本相似度中使用较多的一种方法。后面我们主要介绍余弦相似度。

3.1、基于用户的(UserCF)与基于商品的(ItemCF)推荐方法区别

  1. UserCF: 看重用户相似的小群体的热点,偏重社会化,一般适用于新闻推荐

改进:UserCF-IIF:(类似于TF-IDF的作用):实际业务中用户数量太多,很难对推荐结果做出解释。

  1. ItemCF: 看重个性化,反应用户个人兴趣的传承性,此外商品的更新不能太快,因为实时计算物品相似度矩阵非常耗时,这也是为啥新闻一般不用ItemCF。
    ItemCF在实际业务中用的比较多,可以基于用户的历史购买商品行为对推荐结果做出可理解的解释。

同时,从技术上考虑,UserCF需要维护一个用户相似度的矩阵,而ItemCF需要维护一个物品 相似度矩阵。从存储的角度说,如果用户很多,那么维护用户兴趣相似度矩阵需要很大的空间, 同理,如果物品很多,那么维护物品相似度矩阵代价较大。

来自<<推荐系统>>
ItemCF的计算过程主要分为两步:
  1. 计算物品之间的相似度。【对相似度矩阵按最大值进行归一化可以提高推荐的准确率、覆盖率、多样性】

  2. 根据物品相似度和该用户的历史行为为该用户产生推荐列表【排序】。
    [文章末尾有一份Python实现的Demo]

商品相似度矩阵计算

为特点用户产生推荐列表
该算法的弊端:

这个算法实现起来比较简单,但是在实际应用中会存在一定的问题。

比如一些非常流行的商品可能很多人都喜欢,这种商品推荐给你就没什么意义了,所以计算的时候需要对这种商品加一个权重或者把这种商品去掉。对于一些通用的东西,比如工具书,洗衣液等通用性太强了,推荐也没什么必要了。这些都是推荐系统的脏数据。

此外,当新用户出现时,我们对其兴趣爱好一无所知,这时如何做出推荐是一个很重要的问题。一般在这个时候,我们只是向用户推荐那些普遍反应比较好的物品,也就是说,推荐完全是基于物品的。还有,不是所有的用户都对很多商品给出了评分,很多用户只给少数的书给出了评分,如何处理那些不太表露自己兴趣的用户,也是推荐系统的一个主要问题。

4、工业界的推荐系统

推荐系统在工业界具有广泛的应用,相关的职位招聘也是比较多,算是机器学习相关职位中需求比较多的方向之一。曾经接触过两个互联网企业的推荐系统相关的工作,也算感触到工业界与学术界的一些区别,下面是我自己的一些感触与见闻。

1、数据量

企业级的数据一般都是G量级起步的数据量,很难使用我们参加一些小型竞赛的数据处理方式,python的Pandas等库一般使用很难操作这些业务数据,所以很多推荐系统都是搭建在集群之上的,数据存储可能是基于Hadoop的HDFS等,计算框架一般是Spark或者企业自研的数据平台(阿里的PAI平台...主要任务就是写SQL...羡慕吧)。所以,入职的第一步就是学习hadoop平台与spark的使用,所以,现在后悔上学的时候没有好好的学这些东西啊。

企业级推荐系统
2、实际业务理解

不同的业务场景需要我们根据实际的业务数据深挖数据背后的隐藏信息,大的推荐系统部门,一般都是按照业务部门划分不同的推荐小组,并且推荐小组内有的还会进一步细分任务,例如有专门的基础平台小组、负责召回的、负责排序的。业务逻辑也是需要不断的迭代的,一般每一个工程师每周基本都会上线新的策略,根据实际上线后的效果,不断进行迭代开发。


美图大众点评
3、如何合理的评价推荐系统效果?

参加过一些数据竞赛的推荐系统,一般平台会给出一个评价函数,可能是准确率、召回率等常见评价函数的调和函数。但在实际的业务场景中,却很难给出一个准确的评价函数来评价我们推荐系统的效果。这其中就涉及到推荐系统中多样性与精确性的两难困境

如果要给用户推荐他喜欢的商品,最“保险”的方式就是给他特别流行或者得分特别高的商品,因为这些爆款商品有更可能被喜欢,往坏了说,也很难特别被讨厌。但这种推荐产生的用户体验并不一定好,因为用户很可能已经知道这些热销或流行的产品,所以得到的信息量很少,并且用户不会认为这是 一种“个性化”推荐。

事实上,Mcnee等人已经警告大家,盲目崇拜精确性指标可能会伤害推荐系统,因为这样可能会导致用户得到一些信息量为0的“精准推荐”并且视野变得越来越狭窄。让用户视野变得狭窄是协同过滤算法的一个主要缺陷,这会进一步加剧长尾效应。与此同时,应用个性化推荐技术的商家,也希望推荐中有更多的品类出现,从而激发用户新的购物需求。

遗憾的是,推荐多样的商品和新颖的商品与推荐的精确性之间存在矛盾,因为前者风险很大—推荐一个没人看过或者打分较低的东西,很可能被用户憎恶,从而效果更差。很多时候,这是一个两难的问题,只能通过牺牲多样性来提高精确性,或者牺牲精确性来提高多样性。一种可行之策是直接对推荐列表进行处理,从而提升其多样性。这种方法固然在应用上是有效的,但没有任何理论的基础和优美性可言,只能算一种实用的招数。

一般我们认为,精巧混合精确性高和多样性好的两种算法,可以同时提高算法的多样性和精确性,不需要牺牲任何一方。遗憾的是,还没有办法就这个结果提供清晰的解读和深刻的见解。多样性和精确性之间错综复杂的关系和隐匿其后的竞争,到目前为止还是一个很棘手的难题。

朱郁筱和吕琳媛撰写的《推荐系统评价综述》一文几乎总结了文献中曾经出现过的所有推荐系统指标,这些指标都是基于数据本身的指标,可以认为是第一层次。实际上,在真实应用时,更为重要的是另外两个层次的评价。第二个层次是商业应用上的关键表现指标,如受推荐影响的转化率、购买率、客单价、购买品类数等。第三个层次是用户真实的体验。
绝大部分研究只针对第一个层次的评价指标,而业界真正感兴趣的是第二个层次的评价(比如,到底是哪个指标或者哪些指标组合的结果能够提高用户购买的客单价),而第三个层次最难,没人能知道,只能通过第二层次来估计。因此,如何建立第一层次和第二层次指标之间的关系,就成为了关键。这一步打通了,理论和应用之间的屏障就通一大半了。

5、基于深度学习的推荐系统

其实,上面所讲协同过滤的方法是一种比较传统的方式,仍旧在工业界具有广泛的应用。如今,伴随着机器学习的兴起了非常多的技术被应用到推荐系统中,从传统的机器学习方式LR、GBDT、XGBoost到LightGBM,深度学习从最初利用word2vec用于评估用户的相似度,到CNN、RNN等模型也开始被很多的推荐小组尝试。

爱奇艺的推荐排序技术变迁

深度学习具有优秀的自动提取特征的能力,能够学习多层次的抽象特征表示,并对异质或跨域的内容信息进行学习,可以一定程度上处理推荐系统冷启动问题。

YouTube视频的融合推荐模型

在融合推荐模型的电影推荐系统中:

  1. 首先,使用用户特征和电影特征作为神经网络的输入,其中:
  2. 用户特征融合了四个属性信息,分别是用户ID、性别、职业和年龄。
  3. 电影特征融合了三个属性信息,分别是电影ID、电影类型ID和电影名称。
  4. 对用户特征,将用户ID映射为维度大小为256的向量表示,输入全连接层,并对其他三个属性也做类似的处理。然后将四个属性的特征表示分别全连接并相加。
  5. 对电影特征,将电影ID以类似用户ID的方式进行处理,电影类型ID以向量的形式直接输入全连接层,电影名称用文本卷积神经网络得到其定长向量表示。然后将三个属性的特征表示分别全连接并相加。
  6. 得到用户和电影的向量表示后,计算二者的余弦相似度作为推荐系统的打分。最后,用该相似度打分和用户真实打分的差异的平方作为该回归模型的损失函数。


    融合推荐模型

现在身处工业界,最基础的就是使用协同过滤配合其它的一些排序方法,例如GBDT,基本就能完成推荐的基本功能,基于深度学习的方式现在应用的还没有那么成熟,希望自己今后也能有业务需要我深入的研究一下如何在实际的业务场景中大规模的使用深度学习的推荐系统,毕竟现在我还是一个推荐系统的菜鸟,此外,一直很想写一下对word2vec的认识与理解,关于它在推荐中的应用就留到以后的文章里再介绍吧。

补充:一段协同过滤的Pyhton版Demo[便于理解计算的流程]
# coding:utf-8
from __future__ import division 
import numpy as np
from math import *

# 第一种计算相似度:余弦相似度, 计算两者之间相似度【计算相似度的方法有很多,这里使用余弦相似度】
def cos_sim(x, y):
    """
    :param x(mat): 行向量,可以是用户或商品
    :param y(mat): 行向量,可以是用户或商品
    :return: x 和 y 之间的余弦相似度
    """
    # x 与 y 之间的内积
    inner_product = x * y.T
    norm = np.sqrt(x * x.T) * np.sqrt(y * y.T)
    # 余弦相似度的结果
    return (inner_product / norm)[0, 0]

def similarity(data):
    """
    :param data: 矩阵
    :return: w(mat): 任意两行之间的相似度,相似度矩阵w是一个对称矩阵。在相似度矩阵中约定自身相似度为0。
    """
    # 用户/商品【行数决定方阵的维度】
    m =np.shape(data)[0]
    # 初始化相似度矩阵
    w =np.mat(np.zeros((m, m)))
    for i in range(m):
        for j in range(i, m):
            if j != i:
                # 计算两行之间的相似度[用户-用户 或者 商品-商品]
                w[i, j] = cos_sim(data[i], data[j])
                w[j, i] = w[i, j]
            else:
                w[i, j] = 0
    return w

# 第二种计算相似度:对数似然函数
def obtainK(a,b):
    k11=0
    k12=0
    k21=0
    k22=0
    for i in xrange(len(a)):
        if a[i]==b[i]!=0:
            k11 +=1
        if a[i]==b[i]==0:
            k22 +=1
        if a[i]!=0 and b[i]==0:
            k12 +=1
        if a[i]==0 and b[i]!=0:
            k21 +=1
    return k11,k12,k21,k22


def Entropy(*x):
    sum=0.0
    for i in x:
        sum +=i
    result=0.0
    for j in x:
        if j<0:
            pass
        pinghua=1 if j==0 else 0
        result += j*log((j+pinghua)/sum)
    return result

def loglikelihood(N,a,b):
    k11,k12,k21,k22 = obtainK(a, b)
    rowEntropy=Entropy(k11,k12)+Entropy(k21,k22)
    colEntropy= Entropy(k11,k21)+Entropy(k12,k22)
    matEntropy=Entropy(k11,k12,k21,k22)
    sim=-2*(matEntropy-colEntropy-rowEntropy)
    return sim


# 基于用户的协同过滤
def user_based_recommend(data, w, user):
    """
    :param data(mat): 用户商品矩阵
    :param w(mat): 用户相似度矩阵
    :param user(int): 用户编号
    :return: predict(list): 推荐列表
    """
    # m是用户,n是商品数
    m, n = np.shape(data)
    # 用user这一行:商品信息
    user_product = data[user, ]
    print "用user的买过商品信息:",user_product,m,n
    # 用user0的商品信息: [[4 3 0 5 0]],这说明只有商品3,商品5他没买过

    # 找到用户user没有打分的商品,这是候选的推荐项
    not_score = []
    for i in range(n):
        if user_product[0, i] == 0:
            not_score.append(i)

    # 对没有打分的商品进行预测
    predict = {}
    for x in not_score:
        # 所有用户对该商品的打分信息
        item = data[:, x]
        # 遍历对每一个用户对该商品的评分【这里包含了被推荐人,因为他的权重是0,所以不影响最终的加权权重】
        for i in range(m):
            if item[i, 0] != 0:
                if x not in predict:
                    # 用户i与该用户相似度*用户i对该商品的评分
                    predict[x] = w[user, i] * item[i, 0]
                else:
                    predict[x] = predict[x] + w[user, i] * item[i, 0]

    # 按照预测值大小排序
    return sorted(predict.items(), key=lambda p: p[1], reverse=True)



# 基于商品的协同过滤推荐算法具体实现,如下:
def item_based_recommend(data, w, user):
    """
    :param data(mat): 用户商品矩阵
    :param w(mat): 用户相似度矩阵
    :param user(int): 用户编号
    :return: predict(list): 推荐列表
    """

    # 讲用户商品矩阵转置成商品用户矩阵
    # data = data.T
    m, n = np.shape(data) # m为商品数量, n为用户数量
    # 用user的商品信息
    user_product = data[:, user].T
    # 找到用户user没有打分的商品[在他未购买的里面选出推荐项]
    not_score = []
    # 变量该用户对应的商品,找到没有评分的
    for i in range(m):
        if user_product[0, i] == 0:
            not_score.append(i)

    # 对没有打分的商品进行预测
    predict = {}
    for x in not_score:
        # 该user对该商品的打分信息
        item = user_product
        # 遍历所有g商品
        for i in range(m):
            # 该用户买过这个商品
            if item[0, i] != 0:
                if x not in predict:
                    # 推荐权值 = 该商品与这个商品之间相似度*该用户过的商品的评分
                    predict[x] = w[x, i] * item[0, i]
                else:
                    predict[x] = predict[x] + w[x, i] * item[0, i]

    # 按照预测值大小排序
    return sorted(predict.items(), key=lambda p: p[1], reverse=True)




# 1、定义:我们获取并处理后的数据的格式

# 一行,表示某用户对各商品的评分
# 一列,代表不同用户对同一个商品的打分情况,若给用户没有评价过该商品,则表示这个是未购买过
'''
        商品1,商品2,商品3,商品4,商品5  
用户A   [4, 3, 0, 5, 0],
用户B   [5, 0, 4, 4, 0],
        [4, 0, 5, 0, 3],
        [2, 3, 0, 1, 0],
        [0,4, 2, 0, 5]
'''
"""
一、UserCF
基于用户的协同过滤算法:
首先计算用户-用户之间的相似度
找出该用户u没买过的商品I==候选推荐数据集
遍历所有用户[所有买过I商品的用户U]:求和{ 用户U与用户u的相似度 * 用户U对商品I的评分 }
<利用所有买过候选集商品的用户评分*用户与该用户的相似度-->得出j候选集的得分>

"""

# 用户-商品-评分矩阵
User1 = np.mat([
    [4, 0, 0, 5,1,0,0],
    [5, 0, 4, 4,2,1,3],
    [4, 0, 5, 0,2,0,2],
    [2, 3, 0, 1,3,1,1],
    [0, 4, 2, 0,1,1,4],
])

# print User1
# 用户之间相似性矩阵:计算任意用户之间的余弦距离
w = similarity(np.mat(User1))
print "用户之间相似度:\n",w
# 给U0用户推荐商品:
predict = user_based_recommend(User1, w, 0)
print predict


"""
二、ItemCF
基于项的协同过滤算法:是通过基于项的相似性来进行计算的
计算商品-商品之间的相似度
找出该用户u没买过的商品I==候选推荐数据集
遍历所有商品J[该用户u买过的商品J]==>求和{ 商品I与商品J的相似度 * 用户u对商品J的评分 }
<只利用u自己的购买过的商品,然后根据商品之间的相似度*自己对该商品的评分---得到该候选商品的得分>

"""
# 首先将用户-商品矩阵,转置成商品-用户矩阵
data = User1.T
print "ItemCF:商品-用户-评分:\n",data
# 然后计算商品之间相似性矩阵
w = similarity(data)
print "商品之间相似度:\n",w
# 给U0用户推荐商品:
predict = item_based_recommend(data, w, 0)
print predict


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

推荐阅读更多精彩内容