2011-09-25 2 views
2

나는 특징 테이블의 데이터베이스에서이 같은 데이터 구조 (해시) token_vector라는이 :이두 해시의 내적을 효율적으로

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

이러한 기능 중 25 가지가 있습니다. 첫째, 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이라는 방법이 있습니다. 이 메소드는 두 개의 token_vectors 사이에 내적을 사용합니다. 해시의 내적은 다음과 같이 작동

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

각 중첩 해시 키 (이 경우, A, B 및 C, 그리고 D)는 그 값이 다음에, 서로 쌍으로 곱해 가산있다. 이러한 1*4 = 4를 얻을 수

hash1에서 a의 값이 1 인

hash2에서 a의 값은 4 곱하기입니다. 이러한 2*5 = 10를 얻을 수

hash1에서 b의 값이 2

hash2에서 b의 값은 5 곱하기입니다. 이러한 3*6 = 18 얻을

hash1c에 대한 값이 3

hash2 c에 대한 값은 6 곱하기이다.

hash1d에 대한 값이 존재하지 않는, hash2d의 값이이 경우에 제되면, 제 1 해시 대한 d = 0 세트. 이것들을 곱하면 0*7 = 0이됩니다.

이제 곱한 값을 더합니다. 4 + 10 + 18 + 0 = 32. 이것은 hash1과 hash2의 내적입니다.

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

나는 종종 사용되는 방법이있다, Analysis#topicize. 이 메소드는 매개 변수 인 token_vector을 취하는데, 이것은 위에서와 비슷하게 단지 해시입니다. Analysis#topicizetoken_vector의 내적 값과 25 개의 피쳐 'token_vectors 각각을 가져 와서 feature_vector이라는 25 개 점 상품의 새 벡터를 만듭니다. 은 단지 배열입니다.

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 

당신이 볼 수 있듯이, 그것은 token_vector의 내적을 받아 내가 위에서 레디 스에 입력하고, 배열로 값을 밀어 각 기능의 token_vector : 다음과 같은 코드가 모습입니다.

제 문제는이 방법을 호출 할 때마다 약 18 초가 걸리는 것입니다. 내가 레디 스를 오용 한거야? 문제는 루비 데이터를 루비에로드해서는 안된다는 것입니다. Redis에게 데이터 (token_vector)를 보내고 Ruby 코드로 작성하지 않고 dot_product 기능을 수행하는 Redis 함수를 작성해야합니까?

답변

5

확실하게 프로필을 작성해야하지만 JSON 개체를 직렬화/비 직렬화하는 데 많은 시간을 낭비하고 있다고 생각됩니다.을 JSON 문자열로 바꾸는 대신 Redis에 its own hash type이 있으므로 직접 Redis에 넣지 않으시겠습니까? Hash#flatten가 수행 무엇

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

[ 'a', 1, 'b', 2 ] 같은 배열로 { 'a' => 1, 'b' => 2 } 같은 해시를 회전하고 우리 hmset "의 Redis#hmset 인수로 (이하"m "을 어레이의 각 요소를 보낼 플랫 (*)를 사용 "는"여러 개의 해시 값을 한 번에 설정 "에서처럼"여러 "에 대한 것입니다).

다시 그것을 얻을하려는 경우 다음 밖으로 자동으로 루비 해시 반환 Redis#hgetall, 사용 그러나

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 

을! 해시의 값이 아니라 키만 신경 쓴다면 hgetall 대신 값의 배열을 반환하는 Redis#hvals을 사용하여 좀 더 간소화 할 수 있습니다.

많은 사이클을 보내고있는 두 번째 장소는 소스를 제공하지 않은 locate이지만 Ruby에 점 방식의 메소드를 작성하는 데는 여러 가지 방법이 있으며 그 중 일부는 다른 사람보다 공연. This ruby-talk thread은 몇 가지 중요한 근거를 다룹니다. 포스터 중 하나는, 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 

희망 그게 도움이!

+0

'feature_vector.push (locate (token_vector, REDIS.hgetall ("feature1")))'에 문제가있는 것 같습니다. 'token_vector'는'TokenVector' 클래스이고'REDIS.hgetall ("feature1")'클래스는'Hash' 클래스입니다. 'REDIS.hgetall ("feature1")'클래스가'TokenVector' 클래스가 될 필요가 있습니다. 'TokenVector'는 실제로'Hash'의 서브 클래스입니다. 'REDIS.hgetall ("feature1")'클래스를'Hash'에서'TokenVector'로 바꾸려면 어떻게해야합니까? – Eric

+0

간단히 말해서 :'locate (token_vector, TokenVector [REDIS.hgetall feature1 "])'. TokenVector는 Hash의 서브 클래스이기 (위해) 때문에, 해시의 클래스 메소드'[]'를 상속합니다.이 메소드는, 해시를 인수로서 취할 수가 있습니다. http://www.ruby-doc.org/core/classes/Hash.src/M000716.html –

+0

실제로 답변을 게시했지만 실제로는 이전 의견에 대한 답변입니다.좀 봐 줄래? 감사. – Eric

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>' 

분석 번호는 다음과 같습니다를 찾습니다 여기

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

분석/vectors.rb 라인의 관련 부분을 23- 28 TokenVector # 도트 방법 :

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

어디에 문제가 있는지 잘 모르겠습니다.

+1

오, 내 잘못이야. Redis gem은 문자열 만 반환하므로 각 값을 먼저 숫자로 변환해야합니다. 해시를 사용하면'map & : to_f' 아아를 할 수 없습니다. '(hsh = REDIS.hgetall (...)). merge (hsh) {| key, val | val.to_f}'트릭을해야합니다. 원한다면 TokenVector. []를 오버라이드 (override) 해 원상태로 할 수 있습니다. –

+0

감사합니다. 나는 당신에게 5 점을 줄 수 있었으면 좋겠다. – Eric

+0

좋아요! 속도가 빨라 졌습니까? –

관련 문제