web-dev-qa-db-ja.com

2つの異なる入力サンプルサイズによるKerasマルチタスク学習

共有レイヤーセクションの下にあるKeras [〜#〜] api [〜#〜] のコードを使用してマルチタスク回帰モデルを実装しています。

次のように2つのデータセットがあります。それらをdata_1およびdata_2と呼びましょう。

data_1 : shape(1434, 185, 37)
data_2 : shape(283, 185, 37)

data_1は1434のサンプルで構成され、各サンプルは185文字で、37は一意の文字の総数が37であること、つまりvocab_sizeであることを示します。比較的data_2は283文字で構成されています。

埋め込みレイヤーに渡す前に、次のようにdata_1data_2を2次元のnumpy配列に変換します。

data_1=np.argmax(data_1, axis=2)
data_2=np.argmax(data_2, axis=2)

これにより、データの形状は次のようになります。

print(np.shape(data_1)) 
(1434, 185)

print(np.shape(data_2)) 
(283, 185)

マトリックスの各数値は、インデックス整数を表します。

マルチタスクモデルは次のとおりです。

user_input = keras.layers.Input(shape=((185, )), name='Input_1')
products_input =  keras.layers.Input(shape=((185, )), name='Input_2')

shared_embed=(keras.layers.Embedding(vocab_size, 50, input_length=185))

user_vec_1 = shared_embed(user_input )
user_vec_2 = shared_embed(products_input )


input_vecs = keras.layers.concatenate([user_vec_1, user_vec_2], name='concat')

input_vecs_1=keras.layers.Flatten()(input_vecs)
input_vecs_2=keras.layers.Flatten()(input_vecs)

# Task 1 FC layers
nn = keras.layers.Dense(90, activation='relu',name='layer_1')(input_vecs_1)
result_a = keras.layers.Dense(1, activation='linear', name='output_1')(nn)



# Task 2 FC layers
nn1 = keras.layers.Dense(90, activation='relu', name='layer_2')(input_vecs_2)
result_b = keras.layers.Dense(1, activation='linear',name='output_2')(nn1) 

model = Model(inputs=[user_input , products_input], outputs=[result_a, result_b])

model.compile(optimizer='rmsprop',
              loss='mse',
              metrics=['accuracy'])

モデルは次のように視覚化されます。 enter image description here

次に、次のようにモデルを適合させます。

model.fit([data_1, data_2], [Y_1,Y_2], epochs=10)

エラー:

ValueError: All input arrays (x) should have the same number of samples. Got array shapes: [(1434, 185), (283, 185)]

Kerasで2つの異なるサンプルサイズの入力を使用したり、このエラーを回避してマルチタスク回帰の目標を達成するためのトリックを実行したりする方法はありますか?.

これがテストのための最小の作業コードです

data_1=np.array([[25,  5, 11, 24,  6],
       [25,  5, 11, 24,  6],
       [25,  0, 11, 24,  6],
       [25, 11, 28, 11, 24],
       [25, 11,  6, 11, 11]])

data_2=np.array([[25, 11, 31,  6, 11],
       [25, 11, 28, 11, 31],
       [25, 11, 11, 11, 31]])

Y_1=np.array([[2.33],
       [2.59],
       [2.59],
       [2.54],
       [4.06]])


Y_2=np.array([[2.9],
       [2.54],
       [4.06]])



user_input = keras.layers.Input(shape=((5, )), name='Input_1')
products_input =  keras.layers.Input(shape=((5, )), name='Input_2')

shared_embed=(keras.layers.Embedding(37, 3, input_length=5))
user_vec_1 = shared_embed(user_input )
user_vec_2 = shared_embed(products_input )

input_vecs = keras.layers.concatenate([user_vec_1, user_vec_2], name='concat')


input_vecs_1=keras.layers.Flatten()(input_vecs) 
input_vecs_2=keras.layers.Flatten()(input_vecs)

    nn = keras.layers.Dense(90, activation='relu',name='layer_1')(input_vecs_1)
    result_a = keras.layers.Dense(1, activation='linear', name='output_1')(nn)

    # Task 2 FC layers
    nn1 = keras.layers.Dense(90, activation='relu', name='layer_2')(input_vecs_2)

    result_b = keras.layers.Dense(1, activation='linear',name='output_2')(nn1)

model = Model(inputs=[user_input , products_input], outputs=[result_a, result_b])

model.compile(optimizer='rmsprop',
              loss='mse',
              metrics=['accuracy'])

model.fit([data_1, data_2], [Y_1,Y_2], epochs=10)
9

新しい回答

ここでは、TensorFlow 2を使用したソリューションを作成しています。したがって、必要なものは次のとおりです。

  1. データから形をとる動的入力を定義する

  2. 平均プーリングを使用して、デンスレイヤーディメンションが入力ディメンションから独立します。

  3. 損失を個別に計算する

これが動作するように変更された例です:

## Do this
#pip install tensorflow==2.0.0

import tensorflow.keras as keras
import numpy as np
from tensorflow.keras.models import Model


data_1=np.array([[25,  5, 11, 24,  6],
       [25,  5, 11, 24,  6],
       [25,  0, 11, 24,  6],
       [25, 11, 28, 11, 24],
       [25, 11,  6, 11, 11]])

data_2=np.array([[25, 11, 31,  6, 11],
       [25, 11, 28, 11, 31],
       [25, 11, 11, 11, 31]])

Y_1=np.array([[2.33],
       [2.59],
       [2.59],
       [2.54],
       [4.06]])


Y_2=np.array([[2.9],
       [2.54],
       [4.06]])



user_input = keras.layers.Input(shape=((None,)), name='Input_1')
products_input =  keras.layers.Input(shape=((None,)), name='Input_2')

shared_embed=(keras.layers.Embedding(37, 3, input_length=5))
user_vec_1 = shared_embed(user_input )
user_vec_2 = shared_embed(products_input )

x = keras.layers.GlobalAveragePooling1D()(user_vec_1)
nn = keras.layers.Dense(90, activation='relu',name='layer_1')(x)
result_a = keras.layers.Dense(1, activation='linear', name='output_1')(nn)

# Task 2 FC layers
x = keras.layers.GlobalAveragePooling1D()(user_vec_2)
nn1 = keras.layers.Dense(90, activation='relu', name='layer_2')(x)

result_b = keras.layers.Dense(1, activation='linear',name='output_2')(nn1)

model = Model(inputs=[user_input , products_input], outputs=[result_a, result_b])


loss = tf.keras.losses.MeanSquaredError()
optimizer = tf.keras.optimizers.Adam()

loss_values = []
num_iter = 300
for i in range(num_iter):
    with tf.GradientTape() as tape:
        # Forward pass.
        logits = model([data_1, data_2])          
        loss_value = loss(Y_1, logits[0]) + loss(Y_2, logits[1]) 
        loss_values.append(loss_value)
    gradients = tape.gradient(loss_value, model.trainable_weights)          
    optimizer.apply_gradients(Zip(gradients, model.trainable_weights))

import matplotlib.pyplot as plt
plt.plot(range(num_iter), loss_values)
plt.xlabel("iterations")
plt.ylabel('loss value')

enter image description here

古い回答

あなたの問題はコーディングの問題ではなく、機械学習の問題です!データセットをペアにする必要があります。つまり、Kerasモデルを各ラウンドの両方の入力レイヤーにフィードする必要があります。

ソリューションは、両方のデータセットのサイズが同じになるように、より小さなデータセットをアップサンプリングします。そして、その方法は、データセットのセマンティクスに依存します。もう1つのオプションは、より大きなデータセットをダウンサンプリングすることですが、これはお勧めできません。

非常に基本的な状況で、サンプルがi.i.dであると仮定すると、データセット全体で、次のコードを使用できます。

random_indices = np.random.choice(data_2.shape[0],
data_1.shape[0], replace=True) 

upsampled_data_2 = data_2[random_indices]

したがって、新しいデータセットの新しいバージョンupsampled_data_2、これにはいくつかの繰り返しサンプルが含まれていますが、より大きなデータセットと同じサイズです。

5
moh

あなたがしようとしている場合、それはあなたの質問で明確ではありません:

  1. userproductを取り、その(user, product)ペアについて2つのことを予測する単一のモデルを作成します。 userproductがペアになっていない場合、これが何かを意味していることは明らかではありません(@ matias-valdenegroが指摘したとおり)。他のタイプのランダム要素をペアにする場合( 最初の回答 のように)。各出力が他の入力を無視することを学習することを期待します。これは次と同等です。

  2. 埋め込みレイヤーを共有する2つのモデルを作成します(この場合、連結は意味がありません)。 Y1data1と同じ長さであり、Y2data2と同じ形状である場合、これはおそらく必要なものです。このように、userがある場合はuserモデルを実行でき、productがある場合はproductモデルを実行できます。

あなたは本当に#2が欲しいと思います。それを訓練するためにあなたは次のようなことをすることができます:

for user_batch, product_batch in Zip(user_data.shuffle().repeat(), 
                                     product_data.shuffle().repeat()): 
  user_model.train_on_batch(*user_batch)
  product_model.train_on_batch(*product_batch)
  step = 1
  if step > STEPS:
    break

または、両方を組み合わせたモデルでラップします。

user_result = user_model(user_input)
product_result = product_model(product_input)

model = Model(inputs=[user_input , products_input], 
              outputs=[user_result, product_result])

model.compile(optimizer='rmsprop',
              loss='mse',
              metrics=['accuracy'])

model.fit([data_1, data_2], [Y_1,Y_2], epochs=10)

使用するトレーニング手順に関係なく、2つのモデルの損失が比較できるように出力範囲を正規化する必要があります。最初の手順では、エポックまたはステップを入れ替えます。 2つ目は、2つの損失の加重和に対して単一の勾配ステップを実行します。どの損失の重み付けが最適かを確認することをお勧めします。

2
mdaoust

この答えについては、「ユーザーと製品をペアリングするためのルールはありません」、そして単にランダムにサンプリングしたいとします。

これに対する答えはすでにありますが、固定されたペアがあり、ペアを変更する必要があるため、その答えは偏っています。このためには、ジェネレーターが必要です。

_def generator(data_1, data_2, y1, y2, batchSize):

    l1 = len(data_1)
    l2 = len(data_2)

    #extending data 2 to the same size of data 1
    sampleRelation = l1 // l2 #l1 must be bigger
    if l1 % l2 > 0:
        sampleRelation += 1

    data_2 = np.concatenate([data_2] * sampleRelation, axis=0)
    y2 = np.concatenate([y2] * sampleRelation, axis=0)
    data_2 = data_2[:l1]
    y2 = y2[:l1]


    #batches per Epoch = steps_per_Epoch
    batches = l1 // batchSize
    if l1 % batchSize > 0:
       batches += 1

    while True:  #keras generators must be infinite

        #shuffle indices in every Epoch
        np.random.shuffle(data_1)
        np.random.shuffle(data_2)

        #batch loop
        for b in range(batches):
            start = b*batchSize
            end = start + batchSize

            yield [data_1[start:end], data_2[start:end]], [y1[start:end], y2[start:end]]
_

model.fit_generator(generator(data_1, data_2, Y_1, Y_2, batchSize), steps_per_Epoch = same_number_of_batches_as_inside_generator, ...)を使用してモデルをトレーニングする


私の推測が正しければ、ユーザーと製品の間に関係はなく、単に2つのモデルを同時にトレーニングするだけです。この場合、私はこのアーキテクチャを提案します:

Architecture picture

1
Daniel Möller