Eri*_*ric 2 ruby hash ruby-on-rails redis dot-product
我在功能表中的数据库中有一个这样的数据结构,称为token_vector(哈希):
Feature.find(1).token_vector = { "a" => 0.1, "b" => 0.2, "c" => 0.3 }
Run Code Online (Sandbox Code Playgroud)
这些功能有25个.首先,我将数据输入Redis,其中包括script/console:
REDIS.set( "feature1",
"#{ TokenVector.to_json Feature.find(1).token_vector }"
)
# ...
REDIS.set( "feature25",
"#{ TokenVector.to_json Feature.find(25).token_vector }"
)
Run Code Online (Sandbox Code Playgroud)
TokenVector.to_json首先将哈希转换为JSON格式.存储在Redis中的25个JSON哈希值大约需要8 MB.
我有一个叫做的方法Analysis#locate.此方法采用两个token_vectors之间的点积.哈希的点积如下:
hash1 = { "a" => 1, "b" => 2, "c" => 3 }
hash2 = { "a" => 4, "b" => 5, "c" => 6, "d" => 7 }
Run Code Online (Sandbox Code Playgroud)
散列中的每个重叠键(在这种情况下为a,b和c,而不是d)将它们的值成对地相乘,然后相加.
对于价值a的hash1是1,该值a在hash2是4乘这些获得1*4 = 4.
对于价值b的hash1是2,该值b在hash2为5乘这些获得2*5 = 10.
对于该值c在hash13,对于价值c的hash2是6乘这些获得3*6 = 18.
该值d在hash1不存在,该值d在hash2是7.在这种情况下,设定d = 0为所述第一散列值.将这些乘以得到0*7 = 0.
现在将相乘的值相加.4 + 10 + 18 + 0 = 32.这是hash1和hash2的点积.
Analysis.locate( hash1, hash2 ) # => 32
Run Code Online (Sandbox Code Playgroud)
我有一个经常使用的方法,Analysis#topicize.此方法接受一个参数,token_vector它只是一个哈希,类似于上面.Analysis#topicize取token_vector25个特征中的每个特征的点积token_vectors,并创建这些25个点产品的新矢量,称为feature_vector.A feature_vector只是一个数组.这是代码的样子:
def self.topicize token_vector
feature_vector = FeatureVector.new
feature_vector.push(
locate( token_vector, TokenVector.from_json( REDIS.get "feature1" ) )
)
# ...
feature_vector.push(
locate( token_vector, TokenVector.from_json( REDIS.get "feature25" ) )
)
feature_vector
end
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,它采用了我在上面输入Redis 的点积token_vector和每个特征token_vector,并将值推送到数组中.
我的问题是,每次调用方法时大约需要18秒.我在滥用Redis吗?我认为问题可能是我不应该将Redis数据加载到Ruby中.我是否应该向Redis发送data(token_vector)并编写一个Redis函数来让它执行该dot_product函数,而不是用Ruby代码编写它?
您必须对其进行分析才能确定,但我怀疑您在序列化/反序列化JSON对象时会浪费大量时间.而不是token_vector变成JSON字符串,为什么不将它直接放入Redis,因为Redis有自己的哈希类型?
REDIS.hmset "feature1", *Feature.find(1).token_vector.flatten
# ...
REDIS.hmset "feature25", *Feature.find(25).token_vector.flatten
Run Code Online (Sandbox Code Playgroud)
什么Hash#flatten是将哈希变成类似{ 'a' => 1, 'b' => 2 }的数组[ 'a', 1, 'b', 2 ],然后我们使用splat(*)将数组的每个元素作为参数发送到Redis#hmset("hmset"中的"m"表示"倍数",如"set multiple"哈希值一次").
然后当你想让它恢复使用时Redis#hgetall,会自动返回Ruby Hash:
def self.topicize token_vector
feature_vector = FeatureVector.new
feature_vector.push locate( token_vector, REDIS.hgetall "feature1" )
# ...
feature_vector.push locate( token_vector, REDIS.hgetall "feature25" )
feature_vector
end
Run Code Online (Sandbox Code Playgroud)
然而!由于您只关心哈希值中的值而不是键,因此您可以通过使用来简化事物,它只Redis#hvals返回值的数组,而不是hgetall.
你可能花费很多周期的第二个地方是locate,你没有提供源代码,但是有很多方法可以在Ruby中编写一个dot产品方法,其中一些方法比其他方法更有效.这个ruby-talk主题涵盖了一些有价值的理由.其中一张海报指向NArray,这是一个在C中实现数字数组和向量的库.
如果我正确理解你的代码,可以重新实现这样的东西(先决条件:) gem install narray:
require 'narray'
def self.topicize token_vector
# Make sure token_vector is an NVector
token_vector = NVector.to_na token_vector unless token_vector.is_a? NVector
num_feats = 25
# Use Redis#multi to bundle every operation into one call.
# It will return an array of all 25 features' token_vectors.
feat_token_vecs = REDIS.multi do
num_feats.times do |feat_idx|
REDIS.hvals "feature#{feat_idx + 1}"
end
end
pad_to_len = token_vector.length
# Get the dot product of each of those arrays with token_vector
feat_token_vecs.map do |feat_vec|
# Make sure the array is long enough by padding it out with zeroes (using
# pad_arr, defined below). (Since Redis only returns strings we have to
# convert each value with String#to_f first.)
feat_vec = pad_arr feat_vec.map(&:to_f), pad_to_len
# Then convert it to an NVector and do the dot product
token_vector * NVector.to_na(feat_vec)
# If we need to get a Ruby Array out instead of an NVector use #to_a, e.g.:
# ( token_vector * NVector.to_na(feat_vec) ).to_a
end
end
# Utility to pad out array with zeroes to desired size
def pad_arr arr, size
arr.length < size ?
arr + Array.new(size - arr.length, 0) : arr
end
Run Code Online (Sandbox Code Playgroud)
希望有用!
| 归档时间: |
|
| 查看次数: |
1643 次 |
| 最近记录: |