Tensorflowで自分に合った彼女を探すCNNを実装しようとしてみた(第一回)

# 目的
僕になんで彼女ができないか…
色々調べ回った結果、自分に自信がないことが原因らしい…

じゃあアプローチの成功確率の高そうな自分に合った女性を見つけられれば自信になるのではないだろうか…
街とか歩いてみると、カップルの服装とか顔の系統ってある程度分類わけできる気がするし。

と言うわけで、今回はまず、ファッションの系統を分けるCNNを実装することから始めてみました！

# ファッションの分類わけ
正直、オサレとかよくわからない…
ので、最初はネットに転がっていたファッションテイスト？なる八個の分類で実装してみたんだけど…
これだとデータがうまく分散してなくて、うまく動かない…

と言うことで、4つの軸で系統分けを行うことにしてみた！
###### 第一軸 ~服の色~
モノトーン - 原色っぽい色 - 淡めの色
###### 第二軸 ~環境~
アウトドア　or 街 or 田舎 or インドア or 旅行
(多分、この軸が一個おかしいんだよなぁ)
###### 第三軸 ~性別~
可愛い系 or かっこいい系
###### 第四軸 ~タイプ~
カジュアル or オフィス or アウトドア or スポーツ or 菅田将暉

(こう考えてみると、第二軸と第四軸なんとなくおかしいよなぁ…)


# 制作

#### データの収集
データはファッションの情報サイト的なやつであるWEARからそれぞれの軸に合うデータを収集してみた。
調べてみるとWEARのスクレイピングを既にやってる人がいたので、参考にして実装を行いました。

##### ・ スクレイピング
WEARはどうも、それぞれのファッションタグをIDで管理しているっぽかったので、とりあえず、なんとなくそれっぽい種類のファッションのIDを集めて辞書型でデータを集めさせました。

```python:データのまとまり
classes = {
    'color':{
        'bright':[{'tag_id':4608,'suggest':True,'num':300}],
        'sick':[{'tag_id':14448,'suggest':True,'num':300}],
        'dark':[{'tag_id':10920,'suggest':True,'num':300}]
        },
    'env':{
        'city':[{'tag_id':1887,'suggest':True,'num':100},{'tag_id':6106,'suggest':True,'num':50},{'tag_id':22016,'suggest':True,'num':150}],
        'country':[{'tag_id':12449,'suggest':True,'num':150},{'tag_id':6399,'suggest':True,'num':150}],
        'nature':[{'tag_id':19199,'suggest':True,'num':100},{'tag_id':2186,'suggest':True,'num':50},{'tag_id':4068,'suggest':True,'num':100},{'tag_id':2136,'suggest':False,'num':50},],
        'room':[{'tag_id':68703,'suggest':True,'num':100},{'tag_id':7223,'suggest':True,'num':100},{'tag_id':45955,'suggest':True,'num':100}],
        'travel':[{'tag_id':1194,'suggest':True,'num':50},{'tag_id':10278,'suggest':True,'num':50},{'tag_id':26234,'suggest':True,'num':100},{'tag_id':11615,'suggest':True,'num':50},{'tag_id':44385,'suggest':True,'num':50}]
        },
    'gender':{
        'cool':[{'tag_id':31455,'suggest':True,'num':50},{'tag_id':17914,'suggest':True,'num':100},{'tag_id':3784,'suggest':True,'num':150}],
        'cute':[{'tag_id':78513,'suggest':True,'num':100},{'tag_id':12705,'suggest':False,'num':200},]
        },
    'type':{
        'casual':[{'tag_id':11367,'suggest':True,'num':300}],
        'office':[{'tag_id':2475,'suggest':True,'num':100},{'tag_id':11533,'suggest':True,'num':100},{'tag_id':11428,'suggest':True,'num':100}],
        'outdoor':[{'tag_id':19199,'suggest':True,'num':100},{'tag_id':2186,'suggest':True,'num':50},{'tag_id':4068,'suggest':True,'num':100},{'tag_id':2136,'suggest':False,'num':50},],
        'sports':[{'tag_id':5743,'suggest':True,'num':125},{'tag_id':2219,'suggest':True,'num':125},{'tag_id':11269,'suggest':True,'num':50}],
        'subcul':[{'tag_id':183,'suggest':True,'num':150},{'tag_id':22016,'suggest':True,'num':150}]
        }
    }

```
```python:スクリプト
    photos_dir = 'picture/'+gender+'/'+cat+'/'+type
    files = glob.glob(photos_dir+'/*.jpeg')
    print("=================>"+gender+' _ '+cat+' _ '+type)
    if(len(files)>=300):
        return
    i = 0
    j = 1035
    for page in range(1, 20):

        if(i > num):
            break
        print('============================>> '+str(page)+' _ '+type+' _ '+cat)
        time.sleep(1)
        if(gender == 'men'):
            if(flag):
                r = requests.get('https://wear.jp/men-coordinate/?tag_ids='+str(tag_ids)+'&type_id=2&pageno='+str(page)+'&suggest_flag='+str(s_f_n), timeout=(3.0, 7.5))
            else:
                r = requests.get('https://wear.jp/men-coordinate/?tag_ids='+str(tag_ids)+'&pageno='+str(page), timeout=(3.0, 7.5))
        else:
            if(flag):
                r = requests.get('https://wear.jp/women-coordinate/?tag_ids='+str(tag_ids)+'&type_id=2&pageno='+str(page)+'&suggest_flag='+str(s_f_n), timeout=(3.0, 7.5))
            else:
                r = requests.get('https://wear.jp/women-coordinate/?tag_ids='+str(tag_ids)+'&type_id=2&pageno='+str(page), timeout=(3.0, 7.5))
        time.sleep(2)
        soup = BeautifulSoup(r.text,'lxml')
        imgs = soup.find_all('img', attrs={"data-originalretina": re.compile('^//cdn.wimg.jp/coordinate')})
        imgs += soup.find_all('img', attrs={"data-originalretina": re.compile('^//images.wear2.jp/coordinate')})
        goods = soup.find_all('p', attrs={"class" : "btn"})
        goods = goods[1::2]
        for img, good in zip(imgs, goods):
            i = i+1
            print('No:'+str(i)+'   http:' + img["data-originalretina"])
            good_ = good.find('span')
            if("//cdn.wimg.jp/coordinate" in img["data-originalretina"]):
                url = 'http:' + img["data-originalretina"]
            elif("//images.wear2.jp/coordinate" in img["data-originalretina"]):
                url = 'https:' + img["data-originalretina"]
            r = requests.get(url)
            with open(str('picture/'+gender+'/'+cat+'/'+type+'/')+str(tag_ids)+'_'+str(i)+str('.jpeg'),'wb') as file:
                file.write(r.content)
            url = {'id':i,'url':url}
```
こんな感じでデータを一気に集めてみた！！
データを集めすぎて、ゴミデータが多いこと、それと、これは僕が服に詳しくないからなのか、正直写真に差異ない気がするするんだよなぁ…

参考
【ファッション×テック】WEARのいいね数を予測するアプリを開発した
(https://qiita.com/NakaokaRei/items/03dd5587babcc5f772d3)
WEAR(https://wear.jp/)

#### 学習、推測の実装
で、上記で集めたデータを利用したCNNの実装をおこなっていきました！！

ちなみに殆ど下記のサイトの真似で実装しました（笑）
Deep Learningで犬・猫を分類してみよう 
（https://aiacademy.jp/texts/show/?id=164）

```python:learn.py
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D
from tensorflow.keras.layers import Activation, Dropout, Flatten, Dense

from keras.utils import np_utils
from tensorflow.keras.optimizers import RMSprop

import numpy as np

def load_data(path,num_classes):
    x_train, x_test, y_train, y_test = np.load(path, allow_pickle=True)
    x_train = x_train.astype("float") / 255
    x_test = x_test.astype("float") / 255
    y_train = np_utils.to_categorical(y_train, num_classes)
    y_test  = np_utils.to_categorical(y_test, num_classes)

    return x_train, y_train, x_test, y_test

def train(x, y, x_test, y_test, n, key, k):
    model = Sequential()
    print('On 1st Layer')
    model.add(Conv2D(32, (3, 3),padding='same',input_shape=x.shape[1:]))
    model.add(Activation('relu'))

    print('On 2nd Layer')
    model.add(Conv2D(32,(3,3)))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2,2)))
    model.add(Dropout(0.1))

    print('On 3rd Layer')
    model.add(Conv2D(64,(3,3), padding='same'))
    model.add(Activation('relu'))
    model.add(Conv2D(64,(3,3)))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2,2)))
    model.add(Dropout(0.25))

    print('On 4th Layer(output layer)\n=======>>'+key+' _  '+k)
    model.add(Flatten())
    model.add(Dense(512))
    model.add(Activation('relu'))
    model.add(Dropout(0.45))
    model.add(Dense(n))
    model.add(Activation('softmax'))

    opt = RMSprop(lr=0.001, decay=1e-6)
    model.compile(loss='categorical_crossentropy',optimizer=opt,metrics=['accuracy'])
    model.fit(x, y, batch_size=28, epochs=40)
    model.save('./cnn/'+key+"/"+k+'_cnn.h5')

    return model

def main(path,num_classes,key):
    x_train, y_train, x_test, y_test = load_data(path, num_classes)

    model = train(x_train, y_train, x_test, y_test, num_classes, key, k)
    print(key+'=======> DONE')
# x_train, x_test, y_train, y_test = np.load('./data/men_fashions.npy', allow_pickle=True)
# print(type(x_train))
# exit()

classes = {
    'color':['bright','dark','sick'],
    'env':['city','country','nature','room','travel'],
    'gender':['cool','cute'],
    'type':['casual','office','outdoor','sports','subcul']
    }

dic = {'men':classes,'women':classes}

for key, val in dic.items():
    for k, v in val.items():
        print('Now On '+key+k)
        main(path="./data/"+key+"/"+k+"_fashions.npy", num_classes=len(v), key=key)
```

これで作ったパラメータで、推測をおこなってみたのが

```python
import tensorflow.keras as keras
import sys, os
import numpy as np
from PIL import Image
from tensorflow.keras.models import load_model

imsize = (64, 64)

classes = {
    'color':['bright','dark','sick'],
    'env':['city','country','nature','room','travel'],
    'gender':['cool','cute'],
    'type':['casual','office','outdoor','sports','subcul']
    }

dic = {'men':classes,'women':classes}

def load_img(path):
    img = Image.open(path)
    img = img.convert('RGB')
    img = img.resize(imsize)
    img = np.asarray(img)
    img = img /255.0
    return img

def predict_model(params, test_img):
    model = load_model(params)
    img = load_img(test_img)
    prd = model.predict(np.array([img]))
    prelabel = np.argmax(prd)
    prd[0][prelabel] = 0
    prelabel2 = np.argmax(prd)
    return prelabel,prelabel2

def output(gender, test_img):
    arr = {'color':[0, 0],'env':[0, 0],'gender':[0, 0],'type':[0, 0]}
    for key, val in classes.items():
        params = 'cnn/'+gender+'/'+key+'_cnn.h5'
        arr[key][0], arr[key][1] = predict_model(params, test_img)
    result = [classes[key][val[0]] for key,val in arr.items()]
    result_a = [classes[key][val[1]] for key,val in arr.items()]
    print(result)
    print(result_a)
```

って感じです！！

#### 今回の結果と反省

とりあえず、ファッションの系統分けしようと思ったけど、いや、なんか結果的に全然うまくいかなかった…

##### 反省
・人物切り抜くスクリプトを導入して背景に左右されないようにすること
・データ絶対偏ってる…もっと偏りのないデータの軸を作ること
・服だけじゃなくて、もっと顔だけとか、細かい要素に分けるべきか？

# こっからどうしていくか…
軸の見直しとデータの修正などのデータの修正
カップルに色々聞き取り調査してみることで、どういう服装・顔・雰囲気の人が付き合ってるのか理解すること。
と言うか、こう、なんか、もう少し自分磨きして、女の子とデートにいく努力をした方が近道なのではないだろうか…
なんて思ったりしてしまってるけど、頑張って続きやってきたいな…