我正在尝试从HDFS中将具有自定义.fvecs格式的大文件读取到DataSet中。数据是公开可用的here,它也详细说明了格式。我将给出一个简短的摘要:
文件包含一组存储在原始Little Endian中的向量。每个向量占用4 + d * 4
个字节,其中每个向量的前四个字节是表示向量维数的浮点数,已知为d = 128
。以下d * 4
个字节定义了d
维点内的值。
数据可能包含多达十亿个点,这将永远无法容纳在内存中,因为即使没有额外的开销,这也将需要至少500 GB的内存。如果输入大小不太大,可以使用以下功能读取文件:
// The Point class is just a wrapper for Vector[Float]
def fi_vecs(path: String): Vector[Point] = {
val data_in = new DataInputStream(
new BufferedInputStream(
new FileInputStream(
new File(path))))
// 516 is the number of bytes in a Point.
// For floating points, we have 4 + 4 * 128
val tmpArray = ByteBuffer.allocate(516).array
val buffer = ByteBuffer.wrap(tmpArray)
buffer.order(ByteOrder.LITTLE_ENDIAN)
var tempVec = Vector[Point]()
while (data_in.available > 0) {
data_in.readFully(tmpArray)
buffer.rewind()
val dim = buffer.getInt
if (dim != 128) throw new IOException("Error: Unexpected dimensionality d = " + dim + " of a feature vector.")
var vec = Vector[Float]()
while (vec.size < dim) {
vec = vec :+ buffer.getFloat
}
tempVec = tempVec :+ Point(vec)
}
data_in.close()
tempVec
}
我试图实现自定义FileInputFormat
读取器,但是我不确定如何在不将完整读取的nextRecord
直接保留在内存中的情况下驱动Vector[Point]
函数调用。
任何建议都值得赞赏。