文章内容推荐实践

目录

前言

数据库准备

pom.xml

配置

mahout配置

获取预测值

本项目的思路

数据库数据存储到文件中

预测某用户喜欢的item id

统计所有预测结果到数据库

分析

总结

github

反思


前言

内容推荐在很多公司实践都很常见,由于本博主不会人工zz,所以使用别人的算法mahout算法,其中的基于用户协同算法推荐。

数据库准备

链接:https://pan.baidu.com/s/1vPwGq_gu2i9Y1jf4CV8AIQ 
提取码:jbw3 

pom.xml

        <dependency>
            <groupId>org.apache.mahout</groupId>
            <artifactId>mahout-core</artifactId>
            <version>0.9</version>
        </dependency>

        <dependency>
            <groupId>org.apache.mahout</groupId>
            <artifactId>mahout-integration</artifactId>
            <version>0.9</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

这里使用jpa疯狂造假数据,还有导入mahout包

配置

#通用数据源配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?charset=utf8mb4&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=
# Hikari 数据源专用配置
spring.datasource.hikari.maximum-pool-size=100
spring.datasource.hikari.minimum-idle=20
# JPA 相关配置
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=update

mahout配置

import com.mysql.cj.jdbc.MysqlDataSource;
import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
import org.apache.mahout.cf.taste.impl.model.jdbc.MySQLJDBCDataModel;
import org.apache.mahout.cf.taste.model.DataModel;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.sql.SQLException;

@Configuration
public class MahoutConfig {

    private MysqlDataSource getDataSource() {
        MysqlDataSource dataSource = new MysqlDataSource();
        dataSource.setServerName("127.0.0.1");
        dataSource.setUser("root");
        dataSource.setPassword("xxx");
        dataSource.setDatabaseName("test");
        try {
            dataSource.setServerTimezone("UTC");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return dataSource;
    }

    @Bean(autowire = Autowire.BY_NAME, value = "mySQLDataModel")
    public DataModel getMySQLJDBCDataModel() {
        DataModel dataModel = new MySQLJDBCDataModel(getDataSource(), "test", "uid", "iid",
                "score", "ts");
        return dataModel;
    }

    @Bean(autowire = Autowire.BY_NAME, value = "fileDataModel")
    public DataModel getDataModel() throws IOException {
        URL url = MahoutConfig.class.getClassLoader().getResource("mahout/ratings-1m.data");
        //URL url = MahoutConfig.class.getClassLoader().getResource("mahout/dajitui.data");
        DataModel dataModel = new FileDataModel(new File(url.getFile()));
        return dataModel;
    }
}

mySQLDataModel代表数据库的源,fileDataModel代表文件源,在resource下

MySQLJDBCDataModel里面参数,数据库,数据表名,用户id,喜欢item id,评分,时间

 

获取预测值

URL url = MahoutConfig.class.getClassLoader().getResource("mahout/dajitui.data");
        DataModel dataModel = new FileDataModel(new File(url.getFile()));
        // 相似度计算(皮尔森相似度)
        UserSimilarity similarity = new PearsonCorrelationSimilarity(dataModel);

        // 设置相似用户阈值(或使用NearestNUserNeighborhood)
        UserNeighborhood neighborhood = new ThresholdUserNeighborhood(0.1, similarity, dataModel);

        // 基于以上数据创建推荐器(这里使用的是基于用户的推荐,还有GenericItemBasedRecommender等推荐器)
        Recommender recommender = new GenericUserBasedRecommender(dataModel, neighborhood, similarity);

本项目的思路

为啥到这里才说推荐项目的思路呢?因为我们可以看到上面有两种数据源,文件、数据库。

你们可以试一下,使用数据库会很慢,而且经常说连接数不足,为啥?你一个连接分析7秒多,要不就超过数据库的超时时间,要不就占用连接,导致其他线程在wait for lock

所以我才有将所有数据库的数据存储到文件中,进行分析。

 

数据库数据存储到文件中

@GetMapping("/insert")
    public long doSomething() throws IOException {
        long time = System.nanoTime();
        File file = new File(MahoutConfig.class.getClassLoader().getResource("mahout").getPath() + "/dajitui.data");
        if (!file.exists()) {
            file.createNewFile();
        }
        List<TestDO> testDOList = Optional.ofNullable(testDao.findAll()).orElse(new ArrayList<>());
        FileOutputStream fileOutputStream = new FileOutputStream(file, true);
        for (TestDO testDO : testDOList) {
            String msg = testDO.getUid() + "," + testDO.getIid() + "," + testDO.getScore() + "\n";
            fileOutputStream.write(msg.getBytes());
        }
        fileOutputStream.close();
        long time1 = System.nanoTime();
        return time1 - time;
    }
5201760370纳秒(43.6万数据生成data文件耗费5秒多,6.54 MB)

在target文件夹下class包生成

预测某用户喜欢的item id

@GetMapping("/mahout")
    public List getSomething(int uid) throws TasteException, IOException {
        long time = System.nanoTime();
        URL url = MahoutConfig.class.getClassLoader().getResource("mahout/dajitui.data");
        DataModel dataModel = new FileDataModel(new File(url.getFile()));
        // 相似度计算(皮尔森相似度)
        UserSimilarity similarity = new PearsonCorrelationSimilarity(dataModel);

        // 设置相似用户阈值(或使用NearestNUserNeighborhood)
        UserNeighborhood neighborhood = new ThresholdUserNeighborhood(0.1, similarity, dataModel);

        // 基于以上数据创建推荐器(这里使用的是基于用户的推荐,还有GenericItemBasedRecommender等推荐器)
        Recommender recommender = new GenericUserBasedRecommender(dataModel, neighborhood, similarity);
        List<RecommendedItem> recommendItems = recommender.recommend(uid, 5);
        if (!CollectionUtils.isEmpty(recommendItems)) {
            for (RecommendedItem recommendedItem : recommendItems) {
                ResultDO resultDO = new ResultDO();
                resultDO.setUid(5);
                resultDO.setArticleid(recommendedItem.getItemID());
                resultDO.setValue(new BigDecimal((recommendedItem.getValue())));
                resultDao.save(resultDO);
            }
            long time1 = System.nanoTime();
            System.out.println(time1 - time);
            return recommendItems;
        }
        long time1 = System.nanoTime();
        System.out.println(time1 - time);
        return new ArrayList();
    }
分析一个用户推荐文章耗费496321161纳秒(0.49秒)

统计所有预测结果到数据库

@GetMapping("/tongji")
    public long didSomething() throws IOException, TasteException {
        long time = System.nanoTime();
        URL url = MahoutConfig.class.getClassLoader().getResource("mahout/dajitui.data");
        DataModel dataModel = new FileDataModel(new File(url.getFile()));
        // 相似度计算(皮尔森相似度)
        UserSimilarity similarity = new PearsonCorrelationSimilarity(dataModel);

        // 设置相似用户阈值(或使用NearestNUserNeighborhood)
        UserNeighborhood neighborhood = new ThresholdUserNeighborhood(0.1, similarity, dataModel);

        for (int i = 0; i <= 435995; i += 5) {
            // 基于以上数据创建推荐器(这里使用的是基于用户的推荐,还有GenericItemBasedRecommender等推荐器)
            Recommender recommender = new GenericUserBasedRecommender(dataModel, neighborhood, similarity);
            List<RecommendedItem> recommendItems = null;
            try {
                recommendItems = recommender.recommend(i, 5);
            } catch (Exception e) {
                log.error("查询出错:{}", e.getMessage());
            }

            if (!CollectionUtils.isEmpty(recommendItems)) {
                List<ResultDO> resultDOList = new LinkedList<>();
                for (RecommendedItem recommendedItem : recommendItems) {
                    ResultDO resultDO = new ResultDO();
                    resultDO.setUid(i);
                    resultDO.setArticleid(recommendedItem.getItemID());
                    resultDO.setValue(new BigDecimal((recommendedItem.getValue())));
                    resultDOList.add(resultDO);
                }
                if (!CollectionUtils.isEmpty(resultDOList)) {
                    resultDao.saveAll(resultDOList);
                }
            }
        }
        long time1 = System.nanoTime();
        return time1 - time;
    }

在这里卡到怀疑人生

分析

统计分析结果其实是不怎么现实,一个0.49秒,如果你有10w粉丝,统计下来就是13小时多了。

那开多线程去跑呢?10w粉丝,由于是cpu密集型,线程数跟cpu核心数一样4个,也得跑3个多小时。

总结

除非是有另一台空闲机器去跑,不然对线上服务器压力蛮大的。预想:单独用户id去查询结果缓存起来,用户阅读之后删除或者下次更新推荐的文章。

 

github

点我点我

如果对你有帮助记得点个星星start哦,谢谢老铁

 

反思

我们可以看到才有文件的方式,数据库方式也一样,数据量不能那么high大的,不然对储存都有压力。这个是缺点也很好解决,可以删掉之前的数据呀。不过如果对于大公司来说,推荐这个东西会一直变,所以这里可能会使用人工智障去进行机器学习获得数据模型等等。

其次是在统计上存在困难,毕竟统计基数在哪里,什么东西成上去就瞬间变大。举个栗子:每个中国人给我一块钱,那么我就有13亿,可能还不止。so如果统计1w粉丝,10w粉丝基数在那里,耗费时间也会很长的。个人目前思路:多线程跑,然后不在项目的服务器跑,而是放到另一台服务器跑,避免影响业务运作,这时候分布式系统的优势就体现出来了。不过不是所有的公司都这么有钱,233.

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值