中野智文のブログ

データ・マエショリストのメモ

scikit learn の Kfold, StratifiedKFold, ShuffleSplit の違い

背景

scikit learn の cross validation にて、テスト事例の分割方法に Kfold, StratifiedKFold, ShuffleSplit, StratifiedShuffleSplit というのがある(他にもある)。 その違いがわかりにくい。

Kfold と StratifiedKFold の違い

Kfold は知っているという前提で、StratifiedKFold は、その説明にある通り、

The folds are made by preserving the percentage of samples for each class.

その分割は、サンプルのそれぞれのクラスの割合を保ったまま作られる(超意訳)。

ということらしい。100個のデータがあって、クラス1とクラス0に、それぞれ5個と95個属するとき、それぞれのクラスの割合は、5%と95%である。 よって、その5%と10%を保ったまま分割されるということだ。

でもこのデータを10-Fold にする場合はどうなるのだろうか? 10個づつになるわけだから、0.5個 と 9.5個 に分割しないとクラスの割を保ったままに出来ないはずだが、そのような分割はできない。 だから、そもそもこのようなマイナーなクラスが小さなデータの場合、理想的にクラスの割合が等しく分割することは出来ない。 マイナーなクラスのための機能のような気がするのに、だ。 結果として、

The least populated class in y has only 5 members, which is too few. The minimum number of labels for any class cannot be less than n_folds=10.

といったようなメッセージを見るかもしれない。 これはコード内のコメントを読むと、例外を出す代わりに trim すると言っている。 コードを読み切れていないが、すでに k-fold とはよべないような状況が起きている気がする。 (例えばマイナーなクラスの事例が別の分割(fold)にも重複して使われたり…)

KFold と ShuffleSplit の違い

こちらも、 KFold は知っているという前提で話をすすめる。 KFoldのとの大きな違いは ShuffleSplit は繰り返し再サンプリングする。 よって、k 回行うと重複して同じ事例がでてくるかもしれない。 この ShuffleSplit は事例の数を減らして(ダウンサンプリングして)学習させる際に、別のダウンサンプリングでも結果がそれほど変わらないかを検証する際に使うらしい。

だから、KFold の オプション shuffle=True と同じ結果になるわけではないことに注意。

ちなみに shuffle オプションはデフォルトで、False なので、 shuffle=True をつけないと、事例をランダムにソートしてくれない。

まとめ

  • クラスの割合を保ったまま分割したときは、StratifiedKFold を使う。
  • ただし、マイナーなクラスの事例数が少ない(特に分割数より少ない)と警告が出て、その場合は想定外の結果となるだろう。
  • ShuffleSplit は KFold の shuffle=True ではなく、特にデータが大きい場合のダウンサンプリングのばらつきなどを検証するために使う(複数の分割において重複するかもしれない)

なお、GridSearchCVの説明 によると、推定器か教師データが二値か多クラスの場合、デフォルトで、StratifiedKFold になるそうである。 もちろん、shuffle は Falseになっている。 scikit learn は初心者向けのようで実は…

参考

stackoverflow.com

https://www.kaggle.com/ogrellier/kfold-or-stratifiedkfold

scikit learn の GridSearchCV で検証事例に class_weight(sample_weight) をつける

背景

GridSearchCV で検証事例に sample_weight をつけるような引数はまだ存在しない。

github.com

でも使いたい。(使わないとうまく行かねー)

metrics 関数自体は sample_weight に対応しているんだよね〜。

対応

対応した metrics 関数をつくり、make_scorer で呼び出そう。

以前の記事

nakano-tomofumi.hatenablog.com

にちなんだ例で作る。

from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import KFold
from sklearn.metrics import accuracy_score, log_loss, make_scorer
from sklearn.utils import class_weight 


def balanced_log_loss(y_true, y_pred, cw, eps=1e-15, normalize=True, labels=None):
    sample_weight = [cw[y] for y in y_true]
    return log_loss(y_true=y_true,
                              y_pred=y_pred,
                              eps=eps,
                              sample_weight=sample_weight,
                              labels=labels)



...
    labels = [0, 1] # np.unique(y)
    cw_array = class_weight.compute_class_weight(y=y,
                                                                                    class_weight='balanced',
                                                                                    classes=labels)
    cw = dict(zip(labels, cw_array))
    clf = GridSearchCV(
        LogisticRegression(penalty='l1',
                           class_weight='balanced',
                           fit_intercept=False),
        {'C': [0.01, 0.1, 1, 10, 100]},
        cv=KFold(n_splits=20),
        scoring={'neg_log_loss': make_scorer(balanced_log_loss, 
                                                                        labels=labels,
                                                                        cw=cw, 
                                                                        greater_is_better=False), 
                 'accuracy': 'accuracy'},
        n_jobs=-1,
        refit='neg_log_loss'
    )
    clf.fit(X, y)
...

ちょっとだけ説明すると、balanced_log_loss という独自の metrics 関数を作っている。 その引数に、dict 型の cw というものを用意した。class_weight の略だ。 そして、評価される真の値(y_true)に基づいて、重みを与えている。 もし、予想の値に対して重みを与えたければ、y_pred[cw[y] for y in y_true]の中のy_trueと書き換えれば良い。

さて、この cw だが、学習オプションのclass_weight='balanced'を設定すると内部で呼ばれる compute_class_weight を使っている。 今回は訓練事例全体で cw を求めたが、cross validation 中の訓練事例のみで求めることは難しいようである。 検証事例のみで求めたければ、先の自作関数の中で y_truey_pred から作れば良いのだが、それはナンセンスだろう。

このようなclass weight と、先に作った自作関数を、make_scorer で呼び出す。 今回も neg_log_loss に変換するために、greater_is_better=False オプションを付与して呼び出した。

まとめ

  • 学習時だけでなく、cross validation の評価時にも、sample_weight を使いたい。
  • GridSearchCV にはそのようなオプションはない。
  • そこで、make_scorer から呼び出すことができる自作の metrics 関数にそのようなオプションをつけて対応する。

正規分布の分位数(標準正規分布)を python で求める

背景

あの Z=1.96 などの数値は、正規分布分位数とか、分位点とか、パーセントを使っている場合には、パーセント点とか、百分位点とか、パーセンタイル とかよばれる。 さてこの正規分布の分位数であるが、計算環境が十分でない状況では標準正規分布表というものを使って求めるのが普通だったかもしれない(少なくとも昔の統計の本にはそのようなヒョウがついてくるのが普通だった)が、今なら直接計算できる。

正規分布の分位数の計算

片側95%点(両側だと90%)の求め方

from scipy.stats import norm
norm.ppf(0.95)

片側97.5%点(両側だと95%)の求め方

from scipy.stats import norm
norm.ppf(0.975)

参考

stackoverflow.com

ValueError: y_true contains only one label (0). Please provide the true labels explicitly through the labels argument. というエラー

背景

GridSearchCV をすると、

ValueError: y_true contains only one label (0). Please provide the true labels explicitly through the labels argument.

というようなエラーに遭遇することがある。 これは「評価用のデータに1つの正解ラベルしか含まれていないから、複数の正解ラベルを labels 引数に入れてね」というメッセージなのだが、どこに入れるか分からない。

labels は score 関数のラベルに入れる

結論は表題の通りなのだが、scoring のパラメータに入れる関数のパラメータとなる。scoring パラメータは、string, list, dict, None の形式に対応しているが、 string や list は、score関数の文字列で、引数を指定できない。よってdict形式による関数の指定ということなる。 dict 関数の指定は、文字列と関数の二種類があるが、この例precision のようにstringで指定したら引数を指定できないので、 make_scorer(accuracy_score) のように指定する。この make_scorer は、 sklearn.metrics.make_scorer — scikit-learn 0.19.1 documentation のとおりだが、第一引数に、メトリックス関数を指定できるが、その他にそのメトリックス関数の引数(**kwargsと書かれている)を指定できる。

neg_log_loss に labels=[0, 1] に引数で与える場合。accuracy は一致をみるので、他のクラスの情報をしらなくても問題ないが、neg_log_loss は他に何クラスあるのか知っていないと計算できない模様。

from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import KFold
from sklearn.metrics import accuracy_score, log_loss, make_scorer

...

    clf = GridSearchCV(
        LogisticRegression(penalty='l1')
        {'C': [0.01, 0.1, 1, 10, 100]},
        cv=KFold(n_splits=20),
        scoring={'neg_log_loss': make_scorer(log_loss, labels=[0, 1], greater_is_better=False), 
                 'accuracy': 'accuracy'},
        n_jobs=-1,
        refit='neg_log_loss'
    )
...

greater_is_better=False は、log_loss から、neg_log_loss に変換するときに必要。

まとめ

make_scorer 関数を使って、labels 引数を与えたものを scoring 引数のdict型で与える。

ちなみに、cross validation に、KFold を使わなかったり、cv数(n_splits)を小さくすれば、このエラーを回避することもできる場合もある(検証事例が単独のクラスになりくくなる)が、それは誤った解決法である。

参考

github.com

iOS のUU数を調べる

背景

クッキーが全て取得できない場合でも UU 数を知りたい。

iPhone などのiOSのブラウザでは第三者クッキー(別ドメインのクッキー)が焼けないようになっている。 これは、広告を運用する上では非常に大きな問題である。 なぜなら広告のドメインは広告が表示されるページのドメインではないから、第三者クッキーとなるからである。

今ある手がかりを元になんとか統計を使ってUU数を調べる方法はないのだろうか。

方法

早速始めよう。

用意するデータ

同じ UU がアクセスした頻度を imps とそのUU数を表したデータを準備する。 例えば、一回しかアクセスしていない UU は imps = 1 の UU数に、 2回アクセスした場合には imps = 2 の UU 数にカウントする。

例えば次のようになる。

imps uu
1 141497411
2 1952850
3 929600
4 677190
5 479118
6 377811
7 301407
8 252147
9 212153
10 184203
11 159196
12 141028
13 123652
14 111885
15 101167
16 91964
17 84616
18 78076
19 71527
20 66685
... ...

上記のデータは、ログから次のSQLで取得するイメージである。

select
  imps,
  count(1) uu
from (
 select 
   uuid,
   count(1) imps
 FROM log_data
 group by uuid)
group by  imps

面倒なことに、クッキーが焼けていない UU は全て imps = 1 に混じってカウントされいる。これはクッキーの仕様上仕方ないこと。 (クッキーが焼けた場合のみカウントしようとすると、2回目以降でしか確認できないため、imps = 1 は原理上カウントできないことになってしまうため)

この imps = 1 の中に焼けていないクッキーも含まれていて、この焼けていないクッキーのUU数と焼けているがimps = 1 のUU数を推定するのが、今回の課題となる。

べき乗則を利用する

べき乗則が利用できないかどうか検討する。べき乗則とは、

冪乗則 - Wikipedia

であるが、今回のクッキーの話に適用する。

先の頻度 imps ごとの UU 数を、imps を{i}で、そのUU数を { u(i) } で表すとする。すると、

{ \displaystyle \log u(i) = b \log(i) + a }

の式がべき乗則に従った分布の場合成り立つと考えられる。( {a, b} はなんからの値)

数式だけでは分かりにくいと思うので、先のデータの impsとUU数の関係を二次元のグラフで見てみよう。

https://docs.google.com/spreadsheets/d/e/2PACX-1vT-7jpBFx9UN75_6ASDfvd_5dT35meCj1C-A7wDUUiKjii-ofNg8OPTJXaoMHhIwJnbzBMWNihWdPhh/pubchart?oid=1787407246&format=image

縦軸と横軸は対数にしてあるが、頻度の対数とUU数の対数のプロットが直線上に並んでいる。この様にプロットが直線上に並ぶときべき乗則に従った分布であると考えられる。

この直線を imps = 1 まで伸ばしていけば、頻度 imps = 1 の UU 数が推定できる。それには、この直線(線形関数)を決めているパラメータ {a, b} を求める必要がある。

この {a, b} を求める方法は色々あるが、今回は最小二乗法で求めることにしたい。

線形関数の最小二乗法は解析的に求めることができるので特別な分析ソフトウエアなどは必要としない。 ExcelSQL上で容易に計算することができる。

例えば、SQLで求めるなら次のような感じである。

SELECT 
       (n*d-c*e)/(n*b-e*e) AS a,
       (b*c-d*e)/(n*b-e*e) AS b,
       sum_uu
FROM
  (SELECT 
          sum(ln(imps)*ln(imps)) AS b,
          sum(ln(uu)) AS c,
          sum(ln(imps)*ln(uu)) AS d,
          sum(ln(imps)) AS e,
          count(1) AS n,
          sum(uu) AS sum_uu
   FROM imps_uu)

この例の場合、

{a = -1.434762915, b = 15.41025807}

が得られる。

これを使うと

{ \displaystyle u(i) = e^{a\ln(i) +b} }

だから、

{ \displaystyle
u(1) = e^{-1.434762915 * \ln(1) +15.41025807}
       =  4927084
}

と、クッキーが焼けて頻度が imps = 1 のUU数は、4927084 UUと推定できる。

クッキーが焼けていない UU 数

残るはクッキーが焼けていない UU 数の推定である。クッキーが焼けていないデータは、元のデータの imps =1 のUU数から、先の推定で分かった UU を引いたものである。 この例では、

{ \displaystyle 141497411  - 4927084 = 136570327 }

が、そのインプレッションとなる。これはUU数ではないことに注意する。

さて、このインプレッションがどのようなUU数になるかは、クッキーが焼けている既知の頻度とUU数の分布に従うとしよう。 全インプレッション数がすでに求まっているのであれば、クッキーが焼けている既知のインプレッション数は、全インプレッション数からクッキーが焼けていないインプレッション数を引けば良い。 この例だと、全インプレッション数は、361042790 であるので、

{ \displaystyle  361042790 - 136570327 =  224472463 }

である。これを使って残りのインプレッションがどこに分布するか計算できる。 その分布にしたがった残りのインプレッションの各頻度のインプ数からその頻度で割ることでUU数が求まる。 それらの合計のUU数がクッキーが焼けていないUU数である。すなわち、次の式で表せる。

{ \displaystyle \frac{\widetilde{i}}{\hat{i}} \sum_{i=1} \frac{u(i)}{i} }

ここで、 {\widetilde{i}, \hat{i}} は、それぞれ、 残りのインプレッション数、クッキーが焼けているUUのインプレッション数である。 この例だと、{\widetilde{i} = 136570327, \hat{i} = 224472463} である。 最終的に、クッキーが焼けていない UU 数は、8199399 となった(少数切り捨て)。

全UU数

クッキーが焼けているUU数+クッキーが焼けていない UU 数となる。

クッキーが焼けているUU数は、imps = 2 以上の UU数と、べき乗則によって推定した imps = 1 のUU数である。 この例の場合、

{ \displaystyle  8549777 + 4927084 = 13476861 }

である。また、クッキーが焼けていない UU 数は8199399 だった。

よって、求める全UU数は、

 {13476861 + 8199399 = 21676260}

である。

まとめ

クッキーが取得できない場合に、既に取得できているクッキーの頻度データにもとづき、UU数を推定する手法について述べた。特殊な計算は必要なくSQLExcelだけで計算できることを示した。

この記事は、VOYAGE GROUP Advent Calendar 2017 - Qiita に参加しています。

参考資料

今回使ったデータ(Excel の計算も有り)

docs.google.com

参考サイト

毎回ある確率未満であることを確認する必要なサンプルサイズ

背景

毎回ある確率未満であることを確認するのに、どれだけサンプルサイズが必要だろうか。 例えば、広告枠にて、0.05% 未満のクリック率の広告枠は興味がない場合、0.05% 未満であること自体は信頼上限の計算にて容易に確認できるが、毎日確率が変動する場合、ある日突然 0.05% 以上になっていたりすることがある。これはその広告枠に広告を出稿していなければわからない話だが、本来は興味が無いので、広告は出稿したくない。この場合最低どれほどサンプルを取得するべきなのだろうか。

導出

信頼上限の計算から逆算すれば、信頼上限がある確率未満になるのに必要なサンプルサイズが求められそうである。 Wilson score interval にて信頼上限を求めるとした場合を考える。 Nを全体の試行回数(求めたいサンプルサイズ)、 X を成功回数、Z正規分布の分位数、P をこれ未満にしたい信頼上限とする。このとき、Wilson socre interval の信頼上限は次のようになる。

P = (X+Z*Z/2+Z*sqrt(X*(N-X)/N+Z*Z/4))/(N+Z*Z)

これを maxima で解くことを考える。 以下やってみた。

(%i5) solve((X+Z*Z/2+Z*sqrt(X*(N-X)/N+Z*Z/4))/(N+Z*Z)=P,[N]);
                            2      2
                         N Z  - 4 X  + 4 N X         2    2
                  Z sqrt(-------------------) - 2 P Z  + Z  + 2 X
                                  N
(%o5)        [N = -----------------------------------------------]
                                        2 P

解こうとしたが、sqrt が外せないらしい。そこで危険ではあるが私が式を最小限変形して(過去に何度も間違えた経験あり)、解ける形にする。

{ \displaystyle
\begin{align}
\frac{X+\frac{Z^{2}}{2}+Z\cdot \sqrt{\frac{X\cdot (N-X)}{N}+\frac{Z^{2}}{4} }}{N+Z^{2}}  =  P
\end{align}
}

左辺の分母でかけて、

{ \displaystyle
\begin{align}
X+\frac{Z^{2}}{2}+Z\cdot\sqrt{\frac{X\cdot(N-X)}{N}+\frac{Z^{2}}{4} } =  P\cdot (N+Z^{2})
\end{align}
}

左辺の左の2つの項を右辺へ移動して、二乗根を取り除ける体制に、

{ \displaystyle
\begin{align}
Z\cdot\sqrt{\frac{X\cdot(N-X)}{N}+\frac{Z^{2}}{4} }  =  P\cdot(N+Z^{2}) - (X + \frac{Z^{2}}{2})
\end{align}
}

慎重にやったので問題ないだろう。 左辺の二乗

Z^2*(X*(N-X)/N+Z*Z/4)

から、右辺

P*(N+Z*Z)-(X+Z*Z/2)

を引いたら0という形で、maximasolve() に再びかける。

(%i13) display2d:false;
(%o13) false
(%i14) solve(%o6-%o7^2,[N]);
(%o14) [N = -Z^2,N = -(Z*sqrt((P^2-2*P+1)*Z^2+(4-4*P)*X)+(P-1)*Z^2-2*X)/(2*P),
        N = (Z*sqrt((P^2-2*P+1)*Z^2+(4-4*P)*X)+(1-P)*Z^2+2*X)/(2*P)]

2つの解が出たが、サンプルサイズは正の数であり、後者だろう。よって以下が求める式である。

{ \displaystyle N = \frac{Z\sqrt{(P^{2}-2P+1)Z^{2}+(4-4P)X}+ (1-P)Z^{2}+2X}{ 2P } }

ところで、X はどうやって決めればよいのだろうか。普通の解釈をすれば、X は動的な値である。すなわち、サンプルの取得中に、X は増えていくので、それに応じて N を増やすような形になる。 ところが、多くの場合は動的に取得できることは少ないので、サンプル数は増えるかもしれないが前回の値を直接使えばいいだろう。これは、信頼上限をこえた場合は一部のサンプルではなく、全取得するという前提である。

python で簡単なものを書いてみた。CTRは 0.5% 未満には興味がないという例である。

import math
def sample_size(p, x, z=1.96):
    return (z*math.sqrt((p**2-2*p+1)*z**2+(4-4*p)*x)+(1-p)*z**2+2*x)/(2*p)
def wilson_ucb(n, x, z=1.96):
    return (x+z*z/2+z*math.sqrt(x*(n-x)/n+z*z/4))/(n+z*z)

0クリックの枠

# 1 クリックもなかった広告枠に必要なサンプルサイズは
sample_size(0.005, 0)
764.4784
# そのサンプルサイズで0クリックだった場合のCTRの信頼上限は
wilson_ucb(765, 0)
0.004996607883860603

信頼上限が 0.5% を超えることはなかった。再び配信数(インプレッション)は制限される。

CTR が0.5% よりずっと低い枠

# 1万インプレッションで、5 クリックあった場合、CTR=0.05%だが、この枠のサンプルサイズは?
sample_size(0.005, 5)
2336.485083416135
# 2337 インプレッション取得したところ、1クリックあった。CTRの信頼上限は、
wilson_ucb(2337, 1)
0.002068315942437113

信頼上限が、0.5% を超えることはなかった。再び配信数(インプレッション)は制限される。

# 1クリックだったのでサンプルサイズは?
sample_size(0.005, 1)
1129.0503977791236
# 0 クリックだったのでサンプルサイズは?
sample_size(0.005, 0)
764.4784
# 以上、クリックがなければ上記か、クリックがあれば、2つ上を繰り返す。

CTR が 0.5% 付近の場合

# 1000インプレッションで、5 クリックあった場合。
sample_size(0.005, 5)
2336.485083416135
wilson_ucb(1000, 5)
0.011242912497455987
# 信頼上限は1.1%。この時点で、全取得と思われるが、あえてそれをせずシミュレーション
# 2337 インプレッションで、11 クリックあった場合。
sample_size(0.005, 11)
3934.259645857473

徐々に増えていくことがわかる。いずれ、その広告枠のインプレッション数の最大値に到達することだろう。

結論

もはや適切なサンプルサイズを調べるために、当選確実の計算方法や、統計のページを検索する必要はない。

N = (Z*sqrt((P^2-2*P+1)*Z^2+(4-4*P)*X)+(1-P)*Z^2+2*X)/(2*P)

が、必要なサンプルサイズだ。ここで、Nを全体の試行回数(求めたいサンプルサイズ)、 X を成功回数、Z正規分布の分位数、P をこれ未満にしたい信頼上限とする。

scikit learn の 確率推定のクラスの順序はソートされている。※ただしバイナリクラスの重みは…

背景

liblinear libsvm の重みや、確率推定の配列は、訓練データのクラスラベルの出現順で、決まるという恐ろしい仕様。 出現順というのは、1行目の事例のほうが2行目の事例より前、という出現順。 もちろん学習後、その順序が参照できるように、モデルの出力ファイルにはその出力クラスラベルの順序が書かれてるのだが、 それを読み取るのが面倒だったりするので、学習前に訓練データをクラスラベルでソートしたり tips があったりする[要出典]。

では liblinear libsvm を使っている、scikit learn はどうか?

調査

次のドキュメントを読むと、 sklearn.linear_model.LogisticRegression — scikit-learn 0.19.1 documentation self.classes_ にあるらしい。

そして、 - numpy の unique() https://github.com/scikit-learn/scikit-learn/blob/a2ebb8cfd2d126ad8e6fb36e0bdadba7de8fcd9f/sklearn/preprocessing/label.py#L96

もっと調べた

なんと、バイナリクラスの場合、逆転させていた。

https://github.com/scikit-learn/scikit-learn/blob/ac1b04875331fc291a437025a18ddfefd4051d7c/sklearn/linear_model/base.py#L343-L344

liblinear 内の学習では、0 以上かどうかで判定している。

https://github.com/scikit-learn/scikit-learn/blob/ac1b04875331fc291a437025a18ddfefd4051d7c/sklearn/svm/src/liblinear/linear.cpp#L1760-L1763

では、この prob_col->y はいつどこで決められているのだろう。

https://github.com/scikit-learn/scikit-learn/blob/ac1b04875331fc291a437025a18ddfefd4051d7c/sklearn/svm/src/liblinear/linear.cpp#L2094-L2095

prob->y にて。

https://github.com/scikit-learn/scikit-learn/blob/ac1b04875331fc291a437025a18ddfefd4051d7c/sklearn/svm/src/liblinear/liblinear_helper.c#L141

Yy_ind

https://github.com/scikit-learn/scikit-learn/blob/ac1b04875331fc291a437025a18ddfefd4051d7c/sklearn/svm/base.py#L847-L848

y これが入力。

よって、次の fit_transform がポイントになるが、

https://github.com/scikit-learn/scikit-learn/blob/a2ebb8cfd2d126ad8e6fb36e0bdadba7de8fcd9f/sklearn/preprocessing/label.py#L111-L112

ためしに、[2, 2, 0, 0, 2]unique で呼び出してみた。

>>> import numpy as np
>>> np.unique([2, 2, 0, 0, 2], return_inverse=True)
(array([0, 2]), array([1, 1, 0, 0, 1]))

すなわち、二値(バイナリ)の場合は後者のラベルが、prob_col->y で0以上となることから、後者の重みを学習していることが分かる。

結論

sklearn の係数重みの出力順は、ラベルのソート順(昇順)になっている。

ただし、バイナリクラスの [0, 1] の重みを取ってくる場合は、coef_[0] や、intercept_[0] を取ってくるとラベルが1の方の重みである。