第三章:数据表示
推荐质量和数据的数量和质量关系很大,通常来说,数据越多越好,数据质量越高越好
推荐算法的性能和可扩展性与数据的表示关系很大,因为推荐都是数据密集的(data-intensive)
preference data 表示
单独的一个preference包括user id,item id,preference value的元组,有时候preference value可以没有
preference data是这些元组的一个集合。
在mahout中,Preference是一个接口,需要有一个实现,通常使用GenericPreference
先看Preference代码:
public interface Preference {
/** @return ID of user who prefers the item */
long getUserID();
/** @return item ID that is preferred */
long getItemID();
/**
* @return strength of the preference for that item. Zero should indicate "no preference either way";
* positive values indicate preference and negative values indicate dislike
*/
float getValue();
/**
* Sets the strength of the preference for this item
*
* @param value
* new preference
*/
void setValue(float value);
}
再看GenericPreference代码:
public class GenericPreference implements Preference, Serializable {
private final long userID;
private final long itemID;
private float value;
public GenericPreference(long userID, long itemID, float value) {
Preconditions.checkArgument(!Float.isNaN(value), "NaN value");
this.userID = userID;
this.itemID = itemID;
this.value = value;
}
@Override
public long getUserID() {
return userID;
}
@Override
public long getItemID() {
return itemID;
}
@Override
public float getValue() {
return value;
}
@Override
public void setValue(float value) {
Preconditions.checkArgument(!Float.isNaN(value), "NaN value");
this.value = value;
}
@Override
public String toString() {
return "GenericPreference[userID: " + userID + ", itemID:" + itemID + ", value:" + value + ']';
}
}
看起来没什么特别的,Preconditions是google guava的一个实现,输入不符合类型时会抛出一个异常
使用也很简单了,例如:new GenericPreference(123, 456, 3.0f)
这里存在的一个问题是有效数据20字节,而其overhead却占据了28字节,浪费严重,需要找方法解决,试看mahout是怎么做的
PreferenceArray
public interface PreferenceArray extends Cloneable, Serializable, Iterable<Preference> {
/**
* @return size of length of the "array"
*/
int length();
/**
* @param i
* index
* @return a materialized {@link Preference} representation of the preference at i
*/
Preference get(int i);
/**
* Sets preference at i from information in the given {@link Preference}
*
* @param i
* @param pref
*/
void set(int i, Preference pref);
/**
* @param i
* index
* @return user ID from preference at i
*/
long getUserID(int i);
/**
* Sets user ID for preference at i.
*
* @param i
* index
* @param userID
* new user ID
*/
void setUserID(int i, long userID);
/**
* @param i
* index
* @return item ID from preference at i
*/
long getItemID(int i);
/**
* Sets item ID for preference at i.
*
* @param i
* index
* @param itemID
* new item ID
*/
void setItemID(int i, long itemID);
/**
* @return all user or item IDs
*/
long[] getIDs();
/**
* @param i
* index
* @return preference value from preference at i
*/
float getValue(int i);
/**
* Sets preference value for preference at i.
*
* @param i
* index
* @param value
* new preference value
*/
void setValue(int i, float value);
/**
* @return independent copy of this object
*/
PreferenceArray clone();
/**
* Sorts underlying array by user ID, ascending.
*/
void sortByUser();
/**
* Sorts underlying array by item ID, ascending.
*/
void sortByItem();
/**
* Sorts underlying array by preference value, ascending.
*/
void sortByValue();
/**
* Sorts underlying array by preference value, descending.
*/
void sortByValueReversed();
/**
* @param userID
* user ID
* @return true if array contains a preference with given user ID
*/
boolean hasPrefWithUserID(long userID);
/**
* @param itemID
* item ID
* @return true if array contains a preference with given item ID
*/
boolean hasPrefWithItemID(long itemID);
这个接口的一个实现类是GenericUserPreferenceArray,是针对单个user的所有preference,其内部包含单个userid,一个item id集合和评分集合,这样一个preference所占用的空间减小到12字节稍多,和前面的48字节相比下降了大约四分之三
其属性定义为:
private final long[] ids;
private long id;
private final float[] values;
相应的,针对item的有GenericItemPreferenceArray类
FastByIDMap and FastIDSet
为了节省内存,mahout还使用了FastByIDMap和FastIDSet