Skip to content

Instantly share code, notes, and snippets.

@Taro000
Created November 9, 2018 03:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Taro000/6f14dad198f4e3fb3abfcd81d64c1cda to your computer and use it in GitHub Desktop.
Save Taro000/6f14dad198f4e3fb3abfcd81d64c1cda to your computer and use it in GitHub Desktop.
Finetuningでブランドを分類するAIを作る。 ref: https://qiita.com/Taro000/items/0e4c6694bd61c62b9509
from flask import Flask, render_template, request, redirect, url_for
import os
import uuid
from keras.applications.vgg16 import VGG16
from keras.models import Sequential, Model
from keras.layers import Input, Dropout, Flatten, Dense
from keras.preprocessing import image
import numpy as np
import tensorflow as tf
app = Flask(__name__)
result_dir = './model_detail'
img_height, img_width = 150, 150
channels = 3
# VGG16
input_tensor = Input(shape=(img_height, img_width, channels))
vgg16_model = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)
# FC
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16_model.output_shape[1:]))
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(1, activation='sigmoid'))
# VGG16とFCを接続
model = Model(input=vgg16_model.input, output=top_model(vgg16_model.output))
# 学習済みの重みをロード
model.load_weights(os.path.join(result_dir, 'vgg16_fine.h5'))
model.compile(loss='binary_crossentropy',
optimizer="adam",
metrics=['accuracy'])
graph = tf.get_default_graph()
@app.route("/", methods=["GET", "POST"])
def upload_file():
if request.method == "GET":
return render_template("index.html")
if request.method == "POST":
global graph
f = request.files["file"] # アップロードされた画像を保存
file_path = os.path.join("./datasets/test", str(uuid.uuid4()) + ".jpg")
f.save(file_path)
# 画像を読み込んで4次元テンソルへ変換
with graph.as_default():
img = image.load_img(file_path, target_size=(img_height, img_width))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = x / 255.0
# クラスを予測
pred = model.predict(x)[0]
if pred > 0.5:
predict = "ヨウジヤマモト"
else:
predict = "ユニクロ"
return render_template("index.html", filepath=file_path, predict=predict)
if __name__ == '__main__':
app.run(host="0.0.0.0", port=int("5000"), debug=True)
import os
import re
import random
import shutil
path = "./datasets/クラス別の画像ディレクトリ"
img_list = os.listdir(path)
out_dir = "出力ディレクトリ"
if not (os.path.exists(os.path.join("./datasets", out_dir))):
os.mkdir(os.path.join("./datasets", out_dir))
for i in range(5000):
index = re.search(".jpg", img)
if index:
shutil.move()
while count > 5000:
chosen_img = random.choice(img_list)
if chosen_img != ".DS_Store":
os.remove(os.path.join(path, chosen_img))
from requests import exceptions
import argparse
import requests
import cv2
import os
ap = argparse.ArgumentParser()
ap.add_argument("-q", "--query", required=True,
help="search query to search Bing Image API for")
ap.add_argument("-o", "--output", required=True,
help="path to output directory of images")
args = vars(ap.parse_args())
API_KEY = "YOUR API KEY"
MAX_RESULTS = 250
GROUP_SIZE = 50
URL = "https://api.cognitive.microsoft.com/bing/v7.0/images/search"
EXCEPTIONS = {IOError, FileNotFoundError, exceptions.RequestException, exceptions.HTTPError, exceptions.ConnectionError,
exceptions.Timeout}
term = args["query"]
headers = {"Ocp-Apim-Subscription-Key": API_KEY}
params = {"q": term, "offset": 0, "count": GROUP_SIZE}
print("[INFO] searching Bing API for '{}'".format(term))
search = requests.get(URL, headers=headers, params=params)
search.raise_for_status()
results = search.json()
estNumResults = min(results["totalEstimatedMatches"], MAX_RESULTS)
print("[INFO] {} total results for '{}'".format(estNumResults,
term))
total = 0
for offset in range(0, estNumResults, GROUP_SIZE):
print("[INFO] making request for group {}-{} of {}...".format(
offset, offset + GROUP_SIZE, estNumResults))
params["offset"] = offset
search = requests.get(URL, headers=headers, params=params)
search.raise_for_status()
results = search.json()
print("[INFO] saving images for group {}-{} of {}...".format(
offset, offset + GROUP_SIZE, estNumResults))
for v in results["value"]:
try:
print("[INFO] fetching: {}".format(v["contentUrl"]))
r = requests.get(v["contentUrl"], timeout=30)
ext = v["contentUrl"][v["contentUrl"].rfind("."):]
p = os.path.sep.join([args["output"], "{}{}".format(
str(total).zfill(8), ext)])
f = open(p, "wb")
f.write(r.content)
f.close()
except Exception as e:
if type(e) in EXCEPTIONS:
print("[INFO] skipping: {}".format(v["contentUrl"]))
continue
image = cv2.imread(p)
if image is None:
print("[INFO] deleting: {}".format(p))
os.remove(p)
continue
total += 1
import numpy as np
import os
import glob
from keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array
def draw_img(generator, x, out_dir, img_index):
save_name = "expand_" + str(img_index)
g = generator.flow(x, batch_size=1, save_to_dir=out_dir, save_prefix=save_name, save_format="jpg")
for j in range(10):
g.next()
if __name__ == "__main__":
in_dir = "拡張したい画像のディレクトリ"
out_dir = "拡張後の保存ディレクトリ"
if not (os.path.exists(os.path.join("./datasets", out_dir))):
os.mkdir(os.path.join("./datasets", out_dir))
images = glob.glob(os.path.join("./datasets", in_dir, "*"))
generator = ImageDataGenerator(rotation_range=90)
for i in range(len(images)):
target_img = load_img(images[i])
x = img_to_array(target_img)
x = np.expand_dims(x, axis=0)
draw_img(generator, x, os.path.join("./datasets", out_dir), i)
brand_recog
├── datasets
│   ├── main
│   │   ├── train
│   │   │   ├── uniqlo
│   │   │   └── yohji
│   │   └── validation
│   │   ├── uniqlo
│   │   └── yohji
│   ├── test
│   └── その他画像用ディレクトリ
├── model_detail
├── templates
│   └── webアプリ用htmlファイル
└── その他pythonファイル
$ python bing_img_search.py "検索したい言葉" 画像を保存するディレクトリ
epoch loss acc val_loss val_acc
0 0.336237 0.853759 0.131006 0.955944
1 0.098064 0.971566 0.056027 0.989986
2 0.055371 0.985281 0.034892 0.994017
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>UNIQLO or YOHJI</title>
</head>
<body>
{% if predict %}
<IMG SRC="{{filepath}} " BORDER="1"> 予想:{{predict}} <BR>
<HR>
{% endif %}
ファイルを選択して送信してください<BR>
<form action = "./" method = "POST" enctype = "multipart/form-data">
<input type = "file" name = "file" />
<input type = "submit"/>
</form>
</body>
</html>
import cv2
import os
path = "./datasets/加工した画像のあるディレクトリ"
img_list = os.listdir(path)
if ".DS_Store" in img_list:
img_list.remove(".DS_Store")
out_dir = "出力ディレクトリ"
if not (os.path.exists(os.path.join("./datasets", out_dir))):
os.mkdir(os.path.join("./datasets", out_dir))
for img in img_list:
tgt_img = cv2.imread(os.path.join(path, img))
# convert an image into gray scale
gray_img = cv2.cvtColor(tgt_img, cv2.COLOR_BGR2GRAY)
# get a binary inverse mask
_, mask_inverse = cv2.threshold(gray_img, 150,255, cv2.THRESH_BINARY)
# get a binary mask
mask = cv2.bitwise_not(mask_inverse)
# convert a mask into 3 channels
mask_rgb = cv2.cvtColor(mask, cv2.COLOR_GRAY2RGB)
# apply bitwise and on mask
masked_img = cv2.bitwise_and(tgt_img, mask_rgb)
# replace the cut_out parts with white
mskd_img_replace_white = cv2.addWeighted(masked_img, 1, cv2.cvtColor(mask_inverse, cv2.COLOR_GRAY2RGB), 1, 0)
cv2.imwrite(os.path.join(os.path.join("./datasets", out_dir), "cpied"+img), mskd_img_replace_white)
from keras.preprocessing.image import ImageDataGenerator
from keras.applications import VGG16
from keras.layers import Input, Dense, Flatten, Dropout
from keras.models import Sequential, Model
from keras import optimizers
from keras.utils.vis_utils import plot_model
def save_history(history, result_file):
loss = history.history['loss']
acc = history.history['acc']
val_loss = history.history['val_loss']
val_acc = history.history['val_acc']
nb_epoch = len(acc)
with open(result_file, "w") as fp:
fp.write("epoch\tloss\tacc\tval_loss\tval_acc\n")
for j in range(nb_epoch):
fp.write("%d\t%f\t%f\t%f\t%f\n" % (j, loss[j], acc[j], val_loss[j], val_acc[j]))
if __name__ == "__main__":
# VGG16をダウンロード
input_tensor = Input(shape=(150, 150, 3))
vgg16_model = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)
# 全結合層を構築
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16_model.output_shape[1:]))
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(1, activation='sigmoid'))
# VGG16と全結合層を結合
model = Model(input=vgg16_model.input, output=top_model(vgg16_model.output))
print('vgg16_model:', vgg16_model)
print('top_model:', top_model)
print('model:', model)
model.summary()
plot_model(model, to_file="./model_detail/model.png")
#層の表示
for i in range(len(model.layers)):
print(i, model.layers[i])
# 最後の畳み込み層の直前までfreeze(学習させない)
for layer in model.layers[:15]:
layer.trainable = False
model.summary()
# 参考記事の方がAdamよりもSGDを推奨していたので自分もそっちで
model.compile(loss='binary_crossentropy',
optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
metrics=['accuracy'])
train_datagen = ImageDataGenerator(rescale=1.0 / 255)
val_datagen = ImageDataGenerator(rescale=1.0 / 255)
train_generator = train_datagen.flow_from_directory(
'./datasets/main/train',
target_size=(150, 150),
batch_size=32,
class_mode='binary')
validation_generator = val_datagen.flow_from_directory(
'./datasets/main/validation',
target_size=(150, 150),
batch_size=32,
class_mode='binary')
# 訓練
history = model.fit_generator(
train_generator,
samples_per_epoch=9000,
nb_epoch=3,
validation_data=validation_generator,
nb_val_samples=1000)
# 結果の保存
model.save_weights('./model_detail/vgg16_fine.h5')
save_history(history, './model_detail/history_vgg16_fine.txt')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment