web-dev-qa-db-ja.com

Sklearn train_test_split;トレーニングセットの列から一意の値を保持する

sklearn.model_selection.train_test_split を使用して、トレーニングセットの特定の列のすべての一意の値を保持する方法はありますか。

例を挙げましょう。私が知っている最も一般的なマトリックス因数分解の問題は、 Netflixチャレンジ または Movielens データセットでユーザーが言う映画のレイティングを予測することです。現在、この質問は実際には単一の行列因数分解アプローチに集中していませんが、可能性の範囲内で、ユーザーとアイテムの既知の組み合わせに対してのみ予測を行うグループがあります。

たとえば、Movielens 100kには、943のユニークユーザーと1682のユニーク映画があります。 train_test_splittrain_sizeの比率が高くても(たとえば0.9)、ユニークユーザーと映画の数は同じではありません。私が言及したメソッドのグループは、トレーニングされていない映画またはユーザーに対して0以外は何も予測できないため、これには問題があります。これが私の言いたいことの例です。

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split

ml = pd.read_csv('ml-100k/u.data', sep='\t', names=['User_id', 'Item_id', 'Rating', 'ts'])
ml.head()   
   User_id  Item_id Rating         ts
0      196      242      3  881250949
1      186      302      3  891717742
2       22      377      1  878887116
3      244       51      2  880606923
4      166      346      1  886397596
ml.User_id.unique().size
943
ml.Item_id.unique().size
1682
utrain, utest, itrain, itest, rtrain, rtest = train_test_split(ml, train_size=0.9)
np.unique(utrain).size
943
np.unique(itrain).size
1644

これを何度でも試してみてください。列車のセットに1682本のユニークな映画が登場することはありません。これは、データセット内の評価が1つしかない多くの映画の結果です。幸いなことに、同じことはユーザーには当てはまりません(ユーザーによる評価の最小数は20です)ので、そこでは問題ありません。ただし、トレーニングセットを機能させるには、すべてのユニークな映画を少なくとも1回はトレーニングセットに含める必要があります。さらに、すべてのユーザーまたはすべての映画のエントリが1つしかないため、stratify= kwargを train_test_split に使用することはできません。

私の質問はこれです。

Sklearnにデータセットを分割して、特定の列の一意の値のセットがトレーニングセットに保持されるようにする方法はありますか?

この問題に対する私の基本的な解決策は次のとおりです。

  1. 合計評価数が少ない/ユーザーの項目を分けてください。
  2. これらのめったに評価されないアイテム/ユーザーを除外するデータに train_test_split を作成します(分割サイズ+除外サイズが目的の分割サイズと等しくなるようにします)。
  3. 2つを組み合わせて、最終的な代表的なトレーニングセットを取得します

例:

item_counts = ml.groupby(['Item_id']).size()
user_counts = ml.groupby(['User_id']).size()
rare_items = item_counts.loc[item_counts <= 5].index.values
rare_users = user_counts.loc[user_counts <= 5].index.values
rare_items.size
384
rare_users.size
0
# We can ignore users in this example
rare_ratings = ml.loc[ml.Item_id.isin(rare_items)]
rare_ratings.shape[0]
968
ml_less_rare = ml.loc[~ml.Item_id.isin(rare_items)]
items = ml_less_rare.Item_id.values
users = ml_less_rare.User_id.values
ratings = ml_less_rare.Rating.values
# Establish number of items desired from train_test_split
desired_ratio = 0.9
train_size = desired_ratio * ml.shape[0] - rare_ratings.shape[0]
train_ratio = train_size / ml_less_rare.shape[0]
itrain, itest, utrain, utest, rtrain, rtest = train_test_split(items, users, ratings, train_size=train_ratio)
itrain = np.concatenate((itrain, rare_ratings.Item_id.values))
np.unique(itrain).size
1682
utrain = np.concatenate((utrain, rare_ratings.User_id.values))
np.unique(utrain).size
943
rtrain = np.concatenate((rtrain, rare_ratings.Rating.values))

このアプローチは機能しますが、 train_test_split またはsklearnの別の分割方法で同じことを実現する方法があると感じなければなりません。

警告-データにはユーザーと映画の単一のエントリが含まれています

@ serv-incが提案するアプローチは、すべてのクラスが複数回表されるデータに対して機能します。これは、このデータにも、ほとんどの推奨/ランキングデータセットにも当てはまりません。

21
Grr

あなたが探しているものは層別化と呼ばれています。幸いなことに、sklearnにはそれだけがあります。行をに変更するだけです

itrain, itest, utrain, utest, rtrain, rtest = train_test_split(
     items, users, ratings, train_size=train_ratio, stratify=users)

stratifyが設定されていない場合、データはランダムにシャッフルされます。 http://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html を参照してください

[stratifyis]がNoneでない場合、データは層化された方法で分割され、これをクラスラベルとして使用します。


更新された質問の更新:一意のインスタンスをトレーニングセットに入れることはscikit-learnに組み込まれていないようです。 PredefinedSplit 、または extendStratifiedShuffleSplit を悪用する可能性がありますが、これは単にロールするよりも複雑な場合があります自分の。

3
serv-inc

映画の入力データをグループ化してサンプルを取得し、すべてのサンプルを1つの大きなデータセットに結合できるかもしれません。

# initialize lists
utrain_all =[]
utest_all =[]
itrain_all = []
itest_all = []
rtrain_all = []
rtest__all = []

grp_ml = ml.groupby('Item_id')
for name, group in grp_ml:
 utrain, utest, itrain, itest, rtrain, rtest = train_test_split(group, train_size=0.9)
 utrain_all.append(utrain)
 utest_all.append(utest)
 itrain_all.append(itrain)
 .
 .
 .
0
Mikhail Venkov