2

token_vector私は(ハッシュ)と呼ばれる機能テーブルのデータベースにこのようなデータ構造を持っています:

Feature.find(1).token_vector = { "a" => 0.1, "b" => 0.2, "c" => 0.3 }

これらの機能は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 }"
)

TokenVector.to_json最初にハッシュをJSON形式に変換します。Redisに保存されている25個のJSONハッシュは約8MBを占めます。

と呼ばれるメソッドがありAnalysis#locateます。このメソッドは、2つのtoken_vector間の内積を取ります。ハッシュのドット積は次のように機能します。

hash1 = { "a" => 1, "b" => 2, "c" => 3 }
hash2 = { "a" => 4, "b" => 5, "c" => 6, "d" => 7 }

ハッシュ内の重複する各キー(この場合は、dではなくa、b、およびc)の値がペアごとに乗算され、合計されます。

ainの値hash1は1、inの値ahash24です。これらを乗算して。を取得します1*4 = 4

binの値hash1は2、inの値bhash25です。これらを乗算して。を取得します2*5 = 10

cinの値hash1は3、inの値chash26です。これらを乗算して。を取得します3*6 = 18

dinの値hash1は存在せず、inの値dhash27です。この場合、d = 0最初のハッシュに設定されます。これらを乗算してを取得します0*7 = 0

次に、乗算された値を合計します。4 + 10 + 18 + 0 = 32。これは、hash1とhash2の内積です。

Analysis.locate( hash1, hash2 ) # => 32

よく使われる方法がありAnalysis#topicizeます。token_vectorこのメソッドは、上記と同様に、単なるハッシュであるパラメーターを取ります。と25個の特徴のそれぞれのAnalysis#topicize内積を取り、それらの25個の内積の新しいベクトルを作成します。Aは単なる配列です。コードは次のようになります。token_vectortoken_vectorsfeature_vectorfeature_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

ご覧のとおり、上記のRedisに入力した内積token_vectorと各機能を取得し、値を配列にプッシュします。token_vector

私の問題は、メソッドを呼び出すたびに約18秒かかることです。Redisを誤用していますか?問題は、RedisデータをRubyにロードしてはいけないということだと思います。Rubyコードで記述するのではなく、Redisにデータ(token_vector)を送信し、Redis関数を記述して関数を実行させることになっていますか?dot_product

4

2 に答える 2

5

確実にプロファイリングする必要がありますが、JSONオブジェクトのシリアル化/逆シリアル化で多くの時間を失っていると思います。JSON文字列に変換する代わりに、Redisには独自のハッシュタイプtoken_vectorがあるので、直接Redisに入れてみませんか?

REDIS.hmset "feature1",   *Feature.find(1).token_vector.flatten
# ...
REDIS.hmset "feature25",  *Feature.find(25).token_vector.flatten

のようHash#flattenなハッシュをのような{ 'a' => 1, 'b' => 2 }配列に変換し、[ 'a', 1, 'b', 2 ]splat(*)を使用して配列の各要素を引数として送信しますRedis#hmset(「hmset」の「m」は「複数」を表し、「setmultiple」のように一度に値をハッシュする」)。

次に、それを元に戻したい場合はRedis#hgetall、を使用します。これにより、Rubyハッシュが自動的に返されます。

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

でも!ハッシュからのキーではなく値のみを気にするのでRedis#hvals、の代わりに値の配列を返すだけのを使用することで、物事をもう少し合理化できますhgetall

あなたが多くのサイクルを費やしているかもしれない2番目の場所はあなたがlocateソースを提供していないにありますが、Rubyでドット積メソッドを書く方法はたくさんあり、それらのいくつかは他のものよりもパフォーマンスが高いです。このルビートークスレッドは、いくつかの貴重な根拠をカバーしています。ポスターの1つは、Cで数値配列とベクトルを実装するライブラリであるNArrayを指しています。

私があなたのコードを正しく理解していれば、次のように再実装できます(前提条件:) 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

お役に立てば幸いです。

于 2011-09-25T10:11:39.053 に答える
0

これは実際には答えではなく、私の前のコメントへのフォローアップです。これはおそらくコメントに収まらないからです。Hash/TokenVectorの問題だけが問題ではなかったようです。そうです:

token_vector = Feature.find(1).token_vector
Analysis.locate( token_vector, TokenVector[ REDIS.hgetall( "feature1" ) ] )

このエラーが発生します:

TypeError: String can't be coerced into Float
from /Users/RedApple/S/lib/analysis/vectors.rb:26:in `*'
from /Users/RedApple/S/lib/analysis/vectors.rb:26:in `block in dot'
from /Users/RedApple/S/lib/analysis/vectors.rb:24:in `each'
from /Users/RedApple/S/lib/analysis/vectors.rb:24:in `inject'
from /Users/RedApple/S/lib/analysis/vectors.rb:24:in `dot'
from /Users/RedApple/S/lib/analysis/analysis.rb:223:in `locate'
from (irb):6
from /Users/RedApple/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>'

Analysis#locateは次のようになります。

def self.locate vector1, vector2
  vector1.dot vector2
end

次に、analysis / vectors.rbの23〜28行目の関連部分、TokenVector#dotメソッドを示します。

def dot vector
  inject 0 do |product,item|
    axis, value = item
    product + value * ( vector[axis] || 0 )
  end
end

問題がどこにあるのかわかりません。

于 2011-09-26T00:20:53.717 に答える