DataModel类用于对推荐算法所使用的数据进行准备. 主要完成对不同类型数据的读取, 组织结构 ,分割数据集, 根据配置项对数据进行二值化处理等. 相应的, 定义为DataModel类型的变量在指向子类的方法时, 需要传出Configuration类型的对象. 其中读取文件路径等配置项可以使用setConf来设置.
相应的, 在Java代码中, 可以根据读取数据类型直接生成对应的DataModel. 生成构造器的参数为Configuration的对象. 之后调用buildDataModel进行读取和分割.具体的数据读取过程与分割过程需要在调用buildDataModel之前通过setConf的方式传入对象中. 具体java代码如下
DataModel dataModel = new TextDataModel(conf); dataModel.buildDataModel();
Convertor 读取的数据集文件夹路径通过dfs.data.dir配置项读取,数据集名称通过 data.input.path指定. 之后根据数据类型配置项读取文件夹下所有数据, 并以SparseTensor形式保存. data.column.path 用于指定数据集每一列的含义.data.convertor.binarize.threshold为而值化时所采用的阈值, 即当数值大于阈值时为1, 小于阈值时为0. 当阈值小于0时, 默认不进行二值化处理. 相关配置项示例:
dfs.data.dir=../data data.input.path=filmtrust # dataset name data.column.format=UIR # user-item-rating data.convert.binarize.threshold=-1.0 data.model.format=textLibRec还可以读取UIRT格式的数据, 示例配置项如下:
dfs.data.dir=../data data.input.path=filmtrust data.column.format=UIRT data.convertor.binarize.threshold=-1.0 data.model.format=text当数据列大于4时,需要使用arff格式的数据来进行存储. 相应的推荐配置项为
dfs.data.dir=../data data.input.path=arffsetname data.model.format=arff在Java中的需要使用Configuration 类的set方法来依次写入对象.
Configuration conf = new Configuration(); conf.set("dfs.data.dir","../data"); conf.set("data.input.path","filmtrust"); conf.set("data.column.format","UIRT"); conf.set("data.conver.binariza.threshold","-1.0");实现时可以使用DataModel完成全部操作操作, 也可以单独实例出Convertor对象用于读取数据. 进行读取以及获取相应数据的示例代码如下
Configuration conf = new Configuration(); conf.set(Configured.CONF_DATA_COLUMN_FORMAT, "UIR"); conf.set("inputDataPath", conf.get("dfs.data.dir") + "/test/matrix4by4.txt"); textDataConvertor = new TextDataConvertor(conf.get("inputDataPath")); textDataConvertor.processData(); SparseMatrix preference = textDataConvertor.getPreferenceMatrix(); SparseMatrix datetimeMatrix = textDataConvertor.getDatetimeMatrix();相对应的, 对Arff格式的数据进行读取需要实例ArffDataConvertor对象. 进行读取以及获取相应数据的示例代码如下:
Configuration conf = new Configuration(); conf.set("dfs.data.dir","../data"); conf.set("inputDataPath", conf.get("dfs.data.dir") + "/path/to.arff"); ArffDataConvertor arffLoder = new ArffDataConvertor(conf.get("inputDataPath")); arffLoder.readData(); SparseTensor sparseTensor = arffLoder.getSparseTensor(); ArrayList<ArffInstance> instances = arffLoder.getInstances(); String s1 = arffLoder.getRelationName(); String s2 = arffLoder.getAttributes().get(0).getName();2. Splitter
LibRec中含有数据的划分方式共五类, 将数据集根据一定比例划分为训练集与测试集(及验证集), 留出其中一个作为验证, 给定N个作为验证, K折交叉验证, 以及测试数据集与训练数据集等五种方式. 其中部分分割方式又含有基于user或item等其他分割方式.
根据比例来进行划分数据集.
data.model.splitter=ratio data.splitter.ratio=rating # by rating data.splitter.trainset.ratio=0.8 # resting data used as test set在Java中示例代码如下:
conf.set("data.splitter.ratio", "rating"); conf.set("data.splitter.trainset.ratio", "0.8"); // TextDataConvertor convertor = new TextDataConvertor(args) convertor.processData(); RatioDataSplitter splitter = new RatioDataSplitter(convertor, conf); splitter.splitData();其中data.splitter.ratio的配置可以使用rating, user, userfixed, item, valid, ratingdate, userdate, itemdate. userfixed在数据量较小时可以获得更精确的划分结果. 使用valid时还需要指定data.splitter.validset.ratio. 附带有date的方法为根据数据量在date序列上进行划分. 在时间数值上较大的划分为testset, 时间数值较小的划分为trainset.
随机留出一位或者留出最后一位user或者item来作为测试数据, 剩下的数据作为训练数据
data.model.splitter=loocv data.splitter.loocv=user在Java中示例代码如下:
conf.set("data.splitter.loocv", "user"); convertor.processData(); LOOCVDataSplitter splitter = new LOOCVDataSplitter(convertor, conf); splitter.splitData();其中data.splitter.loocv的配置可以使用user, item, userdate, itemdate. 附带date的配置会先按时间顺序进行排序,选取每个user或item最新的评分记录组成测试集。
保留n个user或item作为测试数据, 剩下的数据作为训练数据.
data.model.splitter=givenn data.splitter.givenn=user data.splitter.givenn.n=10在Java中, 示例代码如下
conf.set("data.splitter.givenn", "user"); conf.set("data.splitter.givenn.n", "1"); convertor.processData(); GivenNDataSplitter splitter = new GivenNDataSplitter(convertor, conf); splitter.splitData();其中data.splitter.givenn的配置可以使用user, item, userdate, itemdate.
K折交叉验证. 即将数据集划分为K份, 每次选择其中一份作为测试数据集, 余下数据作为验证数据集, 共进行K次.每次会进行一次评估, 在K折结束之后会再次对K折计算结果进行一次评估.
data.model.splitter=kcv data.splitter.cv.number=5 # K-fold在Java中, 示例代码如下
convertor.processData(); KCVDataSplitter splitter = new KCVDataSplitter(convertor, conf); for (int i = 1; i <= 6; i++) { // split into 5 parts by default splitter.splitFolds(); splitter.splitData(i); }2.5 testset
预留出部分数据集作为测试数据集. 这里需要设置配置项data.testset.path
也就是指定预留出测试数据的路径. 这个路径要在训练数据的路径之下. 也就是说, 在读取所有数据时, 预留出的测试数据也应该被读取到.
data.model.splitter=testset data.testset.path=nameoftestfile/dir其中Java示例代码如下
conf.set("inputDataPath", conf.get("dfs.data.dir") + "/given-testset"); conf.set(Configured.CONF_DATA_COLUMN_FORMAT, "UIR"); conf.set("data.testset.path", "/given-testset/test/ratings_0.txt"); convertor = new TextDataConvertor(conf.get(Configured.CONF_DATA_COLUMN_FORMAT), conf.get("inputDataPath")); convertor.processData(); GivenTestSetDataSplitter splitter = new GivenTestSetDataSplitter(convertor,conf); splitter.splitData();3. Appender
部分算法(如rste算法)需要引入不同user或者不同item之间的关系矩阵, 因此在LibRec中实现了针对于user-user-relation 或者item-item-relation 的数据读取类.在配置文件中需要增加对配置项data.appender.class
与data.appender.path
的配置.
通过文件的示例配置如下
data.appender.class=social data.appender.path=directory/to/relationData在Java中, 示例代码如下:
String inputPath = conf.get("dfs.data.dir") + "/" + conf.get("data.input.path"); TextDataConvertor textDataConvertor = new TextDataConvertor(inputPath); textDataConvertor.processData(); conf.set("data.appender.path", "test/datamodeltest/trust.txt"); SocialDataAppender dataFeatrue = new SocialDataAppender(conf); dataAppender.setUserMappingData(textDataConvertor.getUserIds()); dataAppender.processData();4. Java code Snippet
如果用于LibRec中的算法, 直接通过conf来设定参数并用于生成相应的DataModel是比较合理的做法. 对于Text与Arff格式的数据可以分别实例相应的DataModel来进行读取.具体实现方式如下:
Configuration conf = new Configuration(); conf.set("dfs.data.dir","../data"); conf.set("data.input.path","filmtrust"); conf.set("data.column.format","UIRT"); conf.set("data.convertor.binariza.threshold","-1.0"); conf.set("data.model.splitter","ratio"); conf.set("data.splitter.ratio","rating"); conf.set("data.splitter.trainset.ratio","0.8"); DataModel dataModel = new TextDataModel(conf); dataModel.buildDataModel();arff格式的实例如下:
Configuration conf = new Configuration(); conf.set("dfs.data.dir","../data"); conf.set("data.input.path","filmtrust"); conf.set("data.column.format","UIR"); conf.set("data.convertor.binariza.threshold","-1.0"); conf.set("data.model.splitter","ratio"); conf.set("data.splitter.ratio","rating"); conf.set("data.splitter.trainset.ratio","0.8"); DataModel dataModel = new ArffDataModel(conf); dataModel.buildDataModel();如果分割方式采用KCV或者Loocv等方式, 在分割数据集之前, 还需要设置测试集的索引. 示例如下
Configuration conf = new Configuration(); conf.set("dfs.data.dir","../data"); conf.set("data.input.path","filmtrust"); conf.set("data.column.format","UIR"); conf.set("data.convertor.binariza.threshold","-1.0"); conf.set("data.model.splitter","kcv"); conf.set("data.splitter.cv.number","5") conf.set("data.splitter.cv.index","1") DataModel dataModel = new TextDataModel(conf); dataModel.buildDataModel();其中配置项data.splitter.cv.index的取值范围为1到data.splitter.cv.number. 之后执行算法时, 只是针对于当前这次训练集测试集合进行计算. 因此如果要统计所有次的结果, 还需要实现循环来分别设置配置项
"data.splitter.cv.index"
并进行分割执行算法.
相应的引入Appender矩阵的, 只用加上相应的配置项即可. 不再单独列出.
在数据读取过程中, 会检测是否已经进行的数据读取, 如果已经读取则不再进行读取和分割. 因此在同一次运行多次执行buildDataModel会重新索引训练集和测试集但是不会重新读取和分割.
目前针对于DataModel没有设置以Convertor和Splitter为参数的构造器以及set方法, 因此单独生成的Convertor, Splitter, 与Appender实例不能传入到DataModel中.