【ファッション×テック】pythonの力でファッショニスタを目指す part2

この記事は[「【ファッション×テック】pythonの力でファッショニスタを目指す part1」](https://qiita.com/cocet33000/items/7beb9e67be6ca07acd15)の続きです。前回見てくださった方ありがとうございます。まだの方は是非。

#前回の振り返り
前回は、有名ファッション情報サイト FASHIONSNAP.COM に投稿されているファッショニスタのストリートスナップから着用ブランドをスクレイピングしたデータを利用し、**「ブランドAを着た時、おすすめのブランドは何？」**という問いに対するベイズ理論に基づいたレコメンド機能の実装しました。

#今回すること
**「ブランドAとブランドBを着た時、おすすめのブランドは何？」**というような複数入力に対応した拡張を行います。

それでは、いってみよう⤴︎⤴︎

#環境
```bash
$ sw_vers
roductName:	Mac OS X
ProductVersion:	10.13.6
BuildVersion:	17G65

$ python -V
Python 3.4.3
```

#レコメンド機能の拡張
ご存知、ベイズの定理はこちら。

```math
P(A|B) = \frac{P(B|A)P(A)}{P(B)} = \frac{P(A,B)}{P(B)}
```
* $P(B)$: 事象$A$が起きる前の、事象$B$の確率(事前確率)
* $P(B|A)$: 事象$A$が起きた後での、事象$B$の確率(事後確率)

今回は、複数のブランドを着用した時のレコメンドを行いたいので、事象Aと事象Bが同時に起きたあとでの事象Cの確率が知りたいわけです。

```math
P(C|A,B) = \frac{P(A,B,C)}{P(A,B)}
```
こうなりますね。では、着用ブランドをリストで渡すと、レコメンドしてくれるように実装してます、コード は以下。

```python
# Bを複数に拡張
def bays2(model, A, B):
    pB = 0
    for bi in B:
        pB =  pB | (model == bi)

    pAB =  (((model == A) | pB ).sum(axis = 1) > len(B)).sum()
    
    if (pB.sum(axis = 1) > len(B) -1).sum()  > 0:
        return  pAB /   (pB.sum(axis = 1) > len(B) -1).sum() 
    else:
        return 'error'

# 複数入力に対応
def predict2(df, brand_df, wear, k = 15):
    prob = []
    
    for brand in brand_df.index:
        prob.append(bays2(df, brand, wear))
    
    if 'error' in prob:
        return 'None'
    
    best_k = sorted(range(len(prob)), key=lambda i: prob[i], reverse=True)[:k]
    return list(map(lambda k:brand_df.index[k], best_k))
```
#評価
では、お試しに代表的なファストファッションブランドを着用した場合のおすすめ3ブランドを推定をしてみましょう。

```bash
>>>predict2(df, brand_df, ['ユニクロ', 'エイチ＆エム'], 3)
['ザラ', 'ヴィンテージ(年代物)', 'メゾン マルジェラ']
```
結果は。

**①ZARA　②ヴィンテージ　③マルジェラ**
1位はこれまたファストファッションブランドのZARA！　ファッショニスタっぽくないファストファッション固めをレコメンドされました。

2位　ヴィンテージ、3位　マルジェラ。確かに、ここら辺をミックスするのはお洒落さん風、いい感じにレコメンドしてくれてます。

では、言われた通りに、ZARAを着用するとして、さらにおすすめを推定してみます。

```bash
>>>predict2(df, brand_df, ['ユニクロ', 'エイチ＆エム','ザラ'], 3)
['ユナイテッドアローズ']
```

結果は、**ユナイテットアローズのみ**、そもそもデータセットにユニクロ、エイチ＆エム、ザラを合わせているコーデが一つしかなく、その人がユナイテッドアローズを合わせていたみたいですね。（~~ブランドだけ見ると、ファッショニスタというよりお洒落覚えたての大学生感~~）

はい。お気づきの通り、この手法だと**データセットに無いブランドの合わせ方をすると推定が不可能**になります。実際に、色々なブランドを入力してレコメンドをしてみました。

**着用ブランドを恣意的に設定した場合**

```bash
>>>predict2(df, brand_df, ['シュプリーム', 'ナイキ'], 3)
['セイコー', 'カシオ', 'コム デ ギャルソン']

>>>predict2(df, brand_df, ['古着(ユーズド)', 'コンバース'], 3)
['ヴィンテージ(年代物)', 'モスコット', 'シュプリーム']

>>>predict2(df, brand_df, ['プラダ', 'ロエベ'], 3)
['ディオール オム', 'セリーヌ', 'バレンシアガ']
```

**着用ブランドをランダムに設定した場合**

```bash
>>>rand_predict(df, brand_df)
weared brand:  ['Vestal', 'ササフラス']
recommend brand:  None

>>>rand_predict(df, brand_df)
weared brand:  ['クリスチャンダダ', 'メゾン ド エフ']
recommend brand:  None

>>>rand_predict(df, brand_df)
weared brand:  ['ジェイ ブランド', 'ストーンアイランド']
recommend brand:  None
```
ランダムの場合の(rand_predict関数)ソースコード 

```python
def rand_predict(df, brand_df, n=2, k=3):
    brand = []
    
    #ランダムにn個のブランドを抽出してリストを作る
    brand_ids = randint(0, brand_df.shape[0], num)  
    for brand_id in brand_ids:
        brand.append(id_to_brand[brand_id])
    
    print('weared brand: ', brand)
    predict_list = predict2(df, brand_df, brand, k) #推定
    print('recommend brand: ', predict_list)
    
    return 0
```

恣意的にそれっぽ合わせ方をすればレコメンドをしてくれますが、適当な合わせ方だと使い物になりません。。

それにしても、シュプリーム、ナイキに対してやたら時計を進めていたり、古着、コンバースにシュプリームだったりなんとも言えない感じです笑

#まとめ
今回は、前回作成した1入力のブランドレコメンド機能を複数入力に拡張しました。

しかし、**データセットに無い組み合わせには全く対応できない**という結果に終わりました（自明）。

次回は、自然言語処理分野の手法に習い、**コーディネイトでのブランドの共起確率から各ブランドのベクトル表現(分散表現)を獲得**することでこの問題を解決したいと思います。