大数据--商品推荐系统项目实现(下)

业务需求

  • 某电商网站首页有猜你喜欢推荐位,该推荐位一次能展示6个商品,推荐内容可以更换四次,共需推荐24个商品。

  • 需要使用协同过滤算法(user CF & Item CF)及基于物品内容的算法进行混合推荐。

  • 一次性展示的6个商品中,从左到右的顺序分别是:

    • 第一位:基于物品的实时推荐结果
    • 第二位:基于用户的离线推荐结果
    • 第三位:基于物品的离线推荐结果
    • 第四位:基于内容的实时推荐结果
    • 第五位:基于物品的实时推荐结果
    • 第六位:基于用户的离线推荐结果
      如有业务需要推广产品,可以指定推广产品出现在某一个位置上。如下图,在第一位上硬推某产品。
      大数据--商品推荐系统项目实现(下)_第1张图片
  • 大型网站的推荐位不仅仅只有一个,需要对每个广告位进行编号,比如猜你喜欢的广告位编号是121

  • 每个推荐位是一个独立的推荐产品,需要对每个广告位开发独立的推荐模型
    每个推荐位需要配置特有的推荐规则和排序规则

  • 为了容错,每个推荐位都需要默认的推荐产品,当推荐系统无法计算正常的结果时,使用默认产品进行推荐。

  • 各个推荐模型推送的商品可以能重复和下线的商品,需要对商品进行进行去重和过滤处理

  • 推荐结果计算完毕之后,将硬推广告放进去。

功能实现分析

  • 用户在商城浏览商品,将用户的浏览记录保存到Cookie,随着用户的请求传送给推荐服务接口。推荐服务接收到用户的基本信息和浏览信息。
    另一种思路,可以通过消费点击流日志,将用户的行为保存到Redis中,推荐服务通过访问Redis获取用户的行为记录。

  • 推荐接口从用户的基本信息中获取到三种推荐结果(离线结果)
    基于历史数据,计算的基于用户的协同过滤的推荐结果,推荐数量24。
    基于用户上一次行为记录,计算的基于物品的协同过滤推荐结果,推荐数量24。这里根据用户对某一个商品的浏览次数进行加权。
    基于用户上一次行为记录,计算的基于内容的推荐结果,推荐数量24。这里根据用户对某一个商品的浏览次数进行加权。

  • 推荐接口从用户的浏览信息中获取用户当前会话的的行为记录,并以此计算基于物品和基于内容的实时推荐结果
    基于用户本次会话的记录,计算基于物品的推荐结果,推荐数量为24
    基于用户本次会话的记录,计算基于内容的推荐结果,推荐数量为24。

  • 对以上的反馈的推荐结果进行排序,排序的过程中对商品去重
    按照业务需求对结果排序,第一位是基于物品的实时推荐结果,依次类推。在排序的过程汇中,需要对推荐的商品进行排序。
    推荐结果生成完毕之后,对整体的推荐结果的产品数量进行补全和删除操作。补全使用该推荐位的默认推荐产品进行补全。

  • 设置业务人员强推的商品,根据业务人员指定的商品序号,替换掉推荐结果中对应序号的推荐商品。

整体架构

大数据--商品推荐系统项目实现(下)_第2张图片

  • 数据平台
    在数据平台上,针对每个用户计算好三个推荐结果,基于用户的推荐结果、基于物品的推荐结果、基于内容的推荐结果。基于物品的相似度、基于内容的相似度。

  • Redis数据缓存:
    通过独立的Java应用将每个用户的推荐结果和基于物品的相似度与基于内容的相似度信息导入到Redis缓存集群中。

  • 获取推荐结果里两种方式;
    一种是已经计算好的离线推荐结果,直接获取即可;
    另一种是根据用户实时的浏览记录计算新的推荐结果。第二种推荐结果主要依赖三种数据,用户的浏览记录、基于物品的相似度、基于内容的相似度。

  • 排序过滤:将推荐的结果按照业务规则进行混合排序及去重等操作。

  • 最终推荐结果:基于业务业务规则对业务推荐的产品进行设置。

代码开发(service项目)

本次代码较多,提供下载地址:点击下载-代码

这里的关系数据由我们写代码生成,模拟真实数据,主要使用下面的类。

  • pom依赖
 
        4.12
        4.2.4.RELEASE
        3.2.8
        1.2.2
        1.2.15
        5.1.32
        1.6.4
        2.4.2
        1.0.9
        4.3.5
        1.2
        2.5
        2.0
        2.5
        3.3.2
        1.3.2
        3.3
        3.4.2-fix
        0.9.1
        1.3.1
        2.7.2
        4.10.3
        2.5.3
        3.4.7
        0.1
        5.11.2
        2.3.23
        2.2.2
    
    
        
        
            joda-time
            joda-time
            ${joda-time.version}
        
        
        
            org.apache.commons
            commons-lang3
            ${commons-lang3.version}
        
        
            org.apache.commons
            commons-io
            ${commons-io.version}
        
        
            commons-net
            commons-net
            ${commons-net.version}
        
        
        
            junit
            junit
            ${junit.version}
            test
        
        
        
            org.slf4j
            slf4j-log4j12
            ${slf4j.version}
        
        
        
            org.mybatis
            mybatis
            ${mybatis.version}
        
        
            org.mybatis
            mybatis-spring
            ${mybatis.spring.version}
        
        
        
            mysql
            mysql-connector-java
            ${mysql.version}
        
        
        
            com.alibaba
            druid
            ${druid.version}
        
        
        
            org.springframework
            spring-context
            ${spring.version}
        
        
            org.springframework
            spring-beans
            ${spring.version}
        
        
            org.springframework
            spring-webmvc
            ${spring.version}
        
        
            org.springframework
            spring-jdbc
            ${spring.version}
        
        
            org.springframework
            spring-aspects
            ${spring.version}
        
        
            org.springframework
            spring-jms
            ${spring.version}
        
        
            org.springframework
            spring-context-support
            ${spring.version}
        
        
        
            jstl
            jstl
            ${jstl.version}
        
        
            javax.servlet
            servlet-api
            ${servlet-api.version}
            provided
        
        
            javax.servlet
            jsp-api
            ${jsp-api.version}
            provided
        
        
        
            commons-fileupload
            commons-fileupload
            ${commons-fileupload.version}
        
        
        
            com.fasterxml.jackson.core
            jackson-databind
            ${jackson.version}
        
        
            mysql
            mysql-connector-java
            5.1.41
        
        
            c3p0
            c3p0
            0.9.1.2
        
        
        
            org.springframework
            spring-jdbc
            4.2.6.RELEASE
        
        
        
            org.apache.lucene
            lucene-core
            4.10.2
        
        
            org.apache.lucene
            lucene-queries
            4.10.2
        
        
            org.apache.lucene
            lucene-test-framework
            4.10.2
        
        
            org.apache.lucene
            lucene-analyzers-common
            4.10.2
        
        
            org.apache.lucene
            lucene-queryparser
            4.10.2
        
        
        
            org.wltea.ik-analyzer
            ik-analyzer
            4.10.2
        
        
            redis.clients
            jedis
            2.9.0
        
        
            commons-lang
            commons-lang
            2.6
        
        
            com.google.code.gson
            gson
            2.8.1
        
        
            junit
            junit
            4.12
        
        
            org.jsoup
            jsoup
            1.10.3
        
    
  • 生成数据的代码,在util包下有一个DataInit类,运行这个类中所有的测试方法,会将数据存入redis缓存。
    PS:记得更改redis的地址。


public class DataInit {

//    public static void main(String[] args) {
//        initProduct();//初始化所有的商品的信息
//        initBaseUserRecommend();//初始化所有的基于用户的信息
//        initBaseItemRecommend();//初始化所有用户基于用户的推荐
//        initBaseItemResult();//初始化物品与物品之间的关联度
//        initBaseContentResult();//初始基于内容的商品关联度
//        initUser();//初始化所有的用户信息
//        initDefaultRecomend();//初始化广告位的默认推荐信息
//    }

    @Test
    public void getData() {
        ShardedJedis jedis = MyShardedJedisPool.getResource();
        //获取基于用户的推荐,用户guyong
        System.out.println("获取用户 guyong ,基于用户的推荐结果======================");
        System.out.println(jedis.hget("recom:guyong", "userCF"));
        System.out.println();
        System.out.println();

        //获取基于物品的推荐,用户guyong
        System.out.println("获取用户 guyong ,基于物品的推荐结果(离线物品)======================");
        System.out.println(jedis.hget("recom:guyong", "itemCF"));
        System.out.println();
        System.out.println();

        //打印基于物品的物品相似度
        System.out.println("打印基于物品的物品相似度======================");
        List productList = JDProduct.getProduct();
        for (Product product : productList) {
            System.out.println(product.getId() + "   " + jedis.get("recom:baseItem:" + product.getId()));
        }
        System.out.println();
        System.out.println();

        //打印基于内容的物品相似度
        System.out.println("打印基于内容的物品相似度======================");
        for (Product product : productList) {
            System.out.println(product.getId() + "   " + jedis.get("recom:baseContent:" + product.getId()));
        }
        System.out.println();
        System.out.println();

        //获取广告位121的默认推荐产品信息
        System.out.println("获取广告位121的默认推荐产品信息======================");
        System.out.println(jedis.hget("recom:default", "121"));
        System.out.println();
        System.out.println();

        //获取所有的商品信息
        System.out.println("获取所有的商品信息======================");
        for (Product product : productList) {
            System.out.println(product.getId() + "   " + jedis.get("recom:prod:" + product.getId()));
        }
        System.out.println();
        System.out.println();
    }


    @Test
    /**
     * 设置一些默认的推荐数据   -----导入到redis中
     */
    public void initDefaultRecomend() {
        //MyShardedJedisPool 将我们的数据保存到redis集群中
        ShardedJedis jedis = MyShardedJedisPool.getResource();
        List productList = JDProduct.getProduct(); //通过网络爬虫获取电商网站的商品数据
//        productList = productList.subList(20, 40);  //截取20个商品作为默认推荐商品列表
        String adId = "121";
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < productList.size(); i++) {
            sb.append(productList.get(i).getId());
            if (i != productList.size() - 1) {
                sb.append(",");
            }
        }

        jedis.hset("recom:default", adId, sb.toString());
        System.out.println("为广告位121设置默认推荐产品成功." + productList.size());

    }

    /**
     * 为用户顾雍初始化基于itemCF的推荐结果
     * recom:guyong:itemCF   12121212,12121212,12121212,12121212
     * recom:guyong:userCF   12121212,12121212,12121212,12121212
     */
    @Test
    public void initBaseItemRecommend() {
        ShardedJedis jedis = MyShardedJedisPool.getResource();
        List productList = JDProduct.getProduct();
//        productList = productList.subList(10, 30);
        String user = "guyong";
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < productList.size(); i++) {
            sb.append(productList.get(i).getId());
            if (i != productList.size() - 1) {
                sb.append(",");
            }
        }
        //为用户推荐基于用户的推荐产品---根据昨天的记录计算好的
        jedis.hset("recom:" + user, "itemCF", sb.toString());
        System.out.println("为用户guyong设置基于物品的推荐产品" + productList.size());
    }


    /**
     * 为用户顾雍初始化基于itemCF的推荐结果
     * recom:guyong   itemCF   12121212,12121212,12121212,12121212,
     * recom:guyong   userCF   12121212,12121212,12121212,12121212
     */
    @Test
    public void initBaseUserRecommend() {
        ShardedJedis jedis = MyShardedJedisPool.getResource();
        List productList = JDProduct.getProduct();
//        productList = productList.subList(0, 20);
        String user = "guyong";
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < productList.size(); i++) {
            sb.append(productList.get(i).getId());
            if (i != productList.size() - 1) {
                sb.append(",");
            }
        }
        jedis.hset("recom:" + user, "userCF", sb.toString());
        System.out.println("为用户guyong设置基于用户的推荐产品" + productList.size());
    }

    /**
     * 保存物品与物品之间的相似度
     * recom:baseItem:productId    121212121:4.2,34343434:3.4
     */
    @Test
    public void initBaseItemResult() {
        ShardedJedis jedis = MyShardedJedisPool.getResource();
        List productList = JDProduct.getProduct();
        int num = productList.size();
        for (int i = 0; i < num; i++) {
            Product product = productList.get(i);
            StringBuffer sb = new StringBuffer();
            for (int j = 0; j < 10; j++) {
                sb.append(productList.get(new Random().nextInt(productList.size())).getId());
                sb.append(":" + (new Random().nextInt(6) + 1));
                if (j != 9) {
                    sb.append(",");
                }
            }
            jedis.set("recom:baseItem:" + product.getId(), sb.toString());
        }
        System.out.println("基于物品:初始化物品与物品之间的相似度关系 " + productList.size());
    }

    /**
     * 保存物品与物品之间的相似度
     * recom:baseContent:productId    121212121:4.2,34343434:3.4
     *
     * 商品属性是否相同
     */
    @Test
    public void initBaseContentResult() {
        ShardedJedis jedis = MyShardedJedisPool.getResource();
        List productList = JDProduct.getProduct();
        int num = productList.size();
        for (int i = 0; i < num; i++) {
            Product product = productList.get(i);
            StringBuffer sb = new StringBuffer();
            for (int j = 0; j < 10; j++) {
                sb.append(productList.get(new Random().nextInt(productList.size())).getId());
                sb.append(":" + (new Random().nextInt(6) + 1));
                if (j != 9) {
                    sb.append(",");
                }
            }
            jedis.set("recom:baseContent:" + product.getId(), sb.toString());
        }
        System.out.println("基于内容:初始化物品与物品之间的相似度关系" + productList.size());
    }

    public void initUser() {
        //todo
    }

    /**
     * 初始化商品信息
     * recom:prod:productid    json,product对象转化的
     */
    @Test
    public void initProduct() {
        ShardedJedis jedis = MyShardedJedisPool.getResource();
        List productList = JDProduct.getProduct();
        for (Product product : productList) {
            String skuid = product.getId();
            jedis.set("recom:prod:" + skuid, new Gson().toJson(product));
        }
        System.out.println("初始化商品信息." + productList.size());
    }

}

网络爬去需要的数据,与推荐商品对应。

代码如下


/**
 * Describe: 请补充类描述
 */
public class JDProduct {

    public static void main(String[] args) throws Exception {
        System.out.println(getProduct());
    }

    public static List getProduct() {
        //String url = "https://sale.jd.com/act/kmdO8ah57uswxKT2.html";

        String url = "https://sale.jd.com/act/iklG3xhS1LRcQqNJ.html";
        List list = getProductsByUrl(url);
        return list;
    }

    private static List getProductsByUrl(String url) {
        List list = new ArrayList();
        try {
            Document doucument = Jsoup.connect(url).get();
            Elements elements = doucument.getElementsByClass("jItem");
            for (Element element : elements) {
                String price = element.select("span.jdNum").text();
                price = price.replaceAll(" ", "");
                if (StringUtils.isBlank(price)) {
                    price = new Random().nextInt(1000) + "";
                }
                String title = element.select("div[title].jDesc").attr("title");
                String producturl = element.select("div[title].jDesc > a").attr("href");
                String skuid = null;
                if (StringUtils.isNotBlank(producturl)) {
                    Pattern pattern = Pattern.compile("\\d+");
                    Matcher matcher = pattern.matcher(producturl);
                    StringBuffer buffer = new StringBuffer();
                    while (matcher.find()) {
                        buffer.append(matcher.group());
                        skuid = buffer.toString();
                    }
                }
                String pic = element.select("img[original]").attr("original");
                if (StringUtils.isNotBlank(skuid) &&
                        StringUtils.isNotBlank(title) &&
                        StringUtils.isNotBlank(price) &&
                        StringUtils.isNotBlank(pic) &&
                        StringUtils.isNotBlank(producturl)) {
                    list.add(new Product(skuid, title, price, producturl, pic));
                }
            }
        } catch (Exception e) {
        }
        return list;
    }

}

运行数据推荐的类,这个类的内部调用较为复杂,需要下载源码梳理,这里中展示运行类的代码


public class RecommendMain {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/applicationContext-service.xml");
        RecommendService recommendService = (RecommendService) ctx.getBean("recommendServiceImpl");
        long start = System.currentTimeMillis();
        //根据用户guyong的浏览记录,为广告位121,推荐24个商品
        // cookie  http协议
        List list = recommendService.recomend("121", "guyong", "5265635,7564497,1039798");
        for (Product product : list) {
            System.out.println(product);
        }
        System.out.println("推荐耗时:" + (System.currentTimeMillis() - start));
    }
}

web项目,用于异步调用推荐的商品

//异步调用的Javascript的代码



启动项目,会展示如下的结果
加载获取的结果如下

id	1739475
name	我的推荐,你的选择
price	199999
img	http://img13.360buyimg.com/n6/s488x350_jfs/t2476/214/1387387908/47235/648d8471/5653ca40N964e7ee4.jpg
url	http://item.jd.com/1739475.html
status	1
1	{…}
id	7564497
name	威露士多效洗衣液瓶装套装13.08斤(3kg洗衣液x2、300g内衣净x1,60ml消毒液x4)
price	420
img	//img12.360buyimg.com/n7/jfs/t18325/89/2493469623/374092/14956afe/5af4f576Nad5f82a9.jpg
url	//item.jd.com/7564497.html
status	1
2	{…}
id	3009211
name	妈妈壹选天然餐具净组合套装(餐具净1kgx4+餐具净500gx4+柔顺剂50gx4) 洗洁精
price	329
img	//img12.360buyimg.com/n7/jfs/t15895/77/935366215/594994/db0efff5/5a44af1dNc5b9d65d.jpg
url	//item.jd.com/3009211.html
status	1
3	{…}
id	1040870
name	威露士(Walch) 衣物家居消毒液 3L 家居衣物除菌液
price	383
img	//img12.360buyimg.com/n7/jfs/t5326/335/2442088867/75285/d768d6af/591ad13fN772df84b.jpg
url	//item.jd.com/1040870.html
status	1

大数据--商品推荐系统项目实现(下)_第3张图片


你可能感兴趣的:(大数据,推荐系统)