เกี่ยวกับ Codelab นี้
1 ข้อควรทราบก่อนที่จะเริ่มต้น
ใน Codelab นี้ คุณจะใช้การปฏิวัติเพื่อแยกประเภทม้าและมนุษย์ คุณจะใช้ TensorFlow ในห้องทดลองนี้เพื่อสร้าง CNN ที่ผ่านการฝึกอบรมให้จดจํารูปภาพม้าและมนุษย์และแยกประเภท
ข้อกำหนดเบื้องต้น
หากยังไม่เคยสร้างการปฏิวัติด้วย TensorFlow มาก่อน คุณอาจต้อง Codelab แบบสร้างคอนกรีตและทําวิธีใช้ร่วมกัน ซึ่งเป็นแพลตฟอร์มเริ่มต้นของการปฏิวัติระบบและการรวมกลุ่มกัน และสร้างโครงข่ายระบบประสาทเทียม (CNN) เพื่อปรับปรุงวิสัยทัศน์ของคอมพิวเตอร์ ซึ่งจะกล่าวถึงวิธีทําให้คอมพิวเตอร์จดจําภาพได้อย่างมีประสิทธิภาพยิ่งขึ้น
สิ่งที่จะได้เรียนรู้
- วิธีฝึกให้คอมพิวเตอร์จดจําฟีเจอร์ในรูปภาพที่ไม่ชัดเจน
สิ่งที่คุณจะสร้าง
- โครงข่ายระบบประสาทเทียมที่แยกความแตกต่างระหว่างรูปภาพม้ากับมนุษย์ได้
สิ่งที่ต้องมี
คุณค้นหาโค้ดสําหรับส่วนที่เหลือของ Codelab ได้อยู่ใน Colab
นอกจากนี้คุณยังต้องติดตั้ง TensorFlow และไลบรารีที่ติดตั้งไว้ใน Codelab ก่อนหน้านี้ด้วย
2 เริ่มต้นใช้งาน: รับข้อมูล
คุณจะทําเช่นนี้ได้โดยสร้างตัวแยกประเภทม้าหรือฮัมมิ่งเพื่อบอกคุณว่าภาพที่ระบุมีม้าหรือมนุษย์ โดยจะมีการฝึกเครือข่ายให้จดจําฟีเจอร์ที่กําหนดว่าภาพใดเป็น คุณจะต้องประมวลผลข้อมูลบางส่วนก่อนจึงจะฝึกได้
ขั้นแรก ให้ดาวน์โหลดข้อมูลดังนี้
!wget --no-check-certificate https://storage.googleapis.com/laurencemoroney-blog.appspot.com/horse-or-human.zip -O /tmp/horse-or-human.zip
โค้ด Python ต่อไปนี้จะใช้ไลบรารีระบบปฏิบัติการเพื่อใช้ไลบรารีระบบปฏิบัติการ คุณจึงเข้าถึงระบบไฟล์และไลบรารีไฟล์ ZIP ได้ จึงช่วยให้คุณแตกข้อมูลได้
import os
import zipfile
local_zip = '/tmp/horse-or-human.zip'
zip_ref = zipfile.ZipFile(local_zip, 'r')
zip_ref.extractall('/tmp/horse-or-human')
zip_ref.close()
ระบบจะดึงเนื้อหาของไฟล์ Zip ไปยังไดเรกทอรีพื้นฐาน /tmp/horse-or-human
ซึ่งมีม้าและไดเรกทอรีย่อยของมนุษย์
กล่าวสั้นๆ คือ ชุดการฝึกคือข้อมูลที่ใช้เพื่อบอกโมเดลโครงข่ายระบบประสาทเทียมที่ " มีลักษณะเป็นม้า" " มีลักษณะคล้ายมนุษย์"
3 ใช้ ImageGenerator เพื่อติดป้ายกํากับและเตรียมข้อมูล
คุณไม่ควรติดป้ายกํากับรูปภาพเป็นม้าหรือมนุษย์อย่างชัดเจน
หลังจากนั้นคุณจะเห็นเนื้อหาชื่อ ImageDataGenerator
ที่ใช้อยู่ โดยจะอ่านรูปภาพจากไดเรกทอรีย่อยและติดป้ายกํากับจากชื่อของไดเรกทอรีย่อยนั้นโดยอัตโนมัติ ตัวอย่างเช่น คุณมีไดเรกทอรีการฝึกอบรมที่มีไดเรกทอรีม้าและไดเรกทอรีมนุษย์ ImageDataGenerator
จะติดป้ายกํากับรูปภาพดังกล่าวให้คุณฟรี กําลังลดขั้นตอนเขียนโค้ด
กําหนดไดเรกทอรีแต่ละรายการ
# Directory with our training horse pictures
train_horse_dir = os.path.join('/tmp/horse-or-human/horses')
# Directory with our training human pictures
train_human_dir = os.path.join('/tmp/horse-or-human/humans')
ดูลักษณะของชื่อไฟล์ในไดเรกทอรีการฝึกม้าและมนุษย์กัน
train_horse_names = os.listdir(train_horse_dir)
print(train_horse_names[:10])
train_human_names = os.listdir(train_human_dir)
print(train_human_names[:10])
ค้นหาจํานวนภาพจากม้าและมนุษย์ทั้งหมดในไดเรกทอรี
print('total training horse images:', len(os.listdir(train_horse_dir)))
print('total training human images:', len(os.listdir(train_human_dir)))
4 สํารวจข้อมูล
ดูรูปภาพสัก 2-3 รูปเพื่อทําความเข้าใจลักษณะของภาพให้ดียิ่งขึ้น
ขั้นแรก ให้กําหนดค่าพารามิเตอร์ matplot
ดังนี้
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
# Parameters for our graph; we'll output images in a 4x4 configuration
nrows = 4
ncols = 4
# Index for iterating over images
pic_index = 0
ตอนนี้แสดงภาพกลุ่มม้า 8 ภาพ และรูปภาพมนุษย์ 8 ภาพ คุณสามารถเรียกใช้เซลล์ใหม่เพื่อดูกลุ่มใหม่ทุกครั้ง
# Set up matplotlib fig, and size it to fit 4x4 pics
fig = plt.gcf()
fig.set_size_inches(ncols * 4, nrows * 4)
pic_index += 8
next_horse_pix = [os.path.join(train_horse_dir, fname)
for fname in train_horse_names[pic_index-8:pic_index]]
next_human_pix = [os.path.join(train_human_dir, fname)
for fname in train_human_names[pic_index-8:pic_index]]
for i, img_path in enumerate(next_horse_pix+next_human_pix):
# Set up subplot; subplot indices start at 1
sp = plt.subplot(nrows, ncols, i + 1)
sp.axis('Off') # Don't show axes (or gridlines)
img = mpimg.imread(img_path)
plt.imshow(img)
plt.show()
ตัวอย่างรูปภาพสําหรับม้าและมนุษย์ในท่าทางและการวางแนวแบบต่างๆ มีดังนี้
5 กําหนดโมเดล
เริ่มต้นกําหนดโมเดล
เริ่มด้วยการนําเข้า TensorFlow:
import tensorflow as tf
จากนั้นเพิ่มเลเยอร์ที่เป็นรูปธรรมและแยกผลลัพธ์ขั้นสุดท้ายออกเป็นชั้นๆ ซึ่งเชื่อมต่อกันอย่างหนาแน่น สุดท้าย เพิ่มเลเยอร์ที่เชื่อมต่ออย่างหนาแน่น
โปรดทราบว่าเนื่องจากคุณพบปัญหาการแยกประเภท 2 คลาส (ปัญหาการแยกประเภทไบนารี) คุณจึงสิ้นสุดเครือข่ายด้วยการเปิดใช้งาน sigmoid เพื่อให้เอาต์พุตของเครือข่ายเป็นสเกลาร์ตัวเดียวระหว่าง 0 ถึง 1 โดยเข้ารหัสความน่าจะเป็นที่รูปภาพปัจจุบันคือคลาส 1 (แทนที่จะเป็นคลาส 0)
model = tf.keras.models.Sequential([
# Note the input shape is the desired size of the image 300x300 with 3 bytes color
# This is the first convolution
tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(300, 300, 3)),
tf.keras.layers.MaxPooling2D(2, 2),
# The second convolution
tf.keras.layers.Conv2D(32, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
# The third convolution
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
# The fourth convolution
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
# The fifth convolution
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
# Flatten the results to feed into a DNN
tf.keras.layers.Flatten(),
# 512 neuron hidden layer
tf.keras.layers.Dense(512, activation='relu'),
# Only 1 output neuron. It will contain a value from 0-1 where 0 for 1 class ('horses') and 1 for the other ('humans')
tf.keras.layers.Dense(1, activation='sigmoid')
])
การเรียกเมธอด model.summary()
จะพิมพ์ข้อมูลสรุปของเครือข่าย
model.summary()
ดูผลลัพธ์ได้ที่นี่
Layer (type) Output Shape Param # ================================================================= conv2d (Conv2D) (None, 298, 298, 16) 448 _________________________________________________________________ max_pooling2d (MaxPooling2D) (None, 149, 149, 16) 0 _________________________________________________________________ conv2d_1 (Conv2D) (None, 147, 147, 32) 4640 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 73, 73, 32) 0 _________________________________________________________________ conv2d_2 (Conv2D) (None, 71, 71, 64) 18496 _________________________________________________________________ max_pooling2d_2 (MaxPooling2 (None, 35, 35, 64) 0 _________________________________________________________________ conv2d_3 (Conv2D) (None, 33, 33, 64) 36928 _________________________________________________________________ max_pooling2d_3 (MaxPooling2 (None, 16, 16, 64) 0 _________________________________________________________________ conv2d_4 (Conv2D) (None, 14, 14, 64) 36928 _________________________________________________________________ max_pooling2d_4 (MaxPooling2 (None, 7, 7, 64) 0 _________________________________________________________________ flatten (Flatten) (None, 3136) 0 _________________________________________________________________ dense (Dense) (None, 512) 1606144 _________________________________________________________________ dense_1 (Dense) (None, 1) 513 ================================================================= Total params: 1,704,097 Trainable params: 1,704,097 Non-trainable params: 0
คอลัมน์รูปร่างเอาต์พุตแสดงขนาดของแผนที่ฟีเจอร์ซึ่งเปลี่ยนแปลงไปในแต่ละเลเยอร์ที่ต่อเนื่องกัน เลเยอร์การปฏิวัติจะลดขนาดฟีเจอร์ของแผนที่ลงเล็กน้อยเนื่องจากระยะห่างจากขอบและการรวมแต่ละเลเยอร์ลงครึ่งหนึ่งของมิติข้อมูล
6 คอมไพล์โมเดล
ถัดไป ให้กําหนดค่าข้อมูลจําเพาะสําหรับการฝึกโมเดล ฝึกโมเดลด้วยการสูญเสีย binary_crossentropy
เนื่องจากปัญหาเกี่ยวกับการแยกประเภทไบนารีและการเปิดใช้งานครั้งสุดท้ายคือซิกโมด (สําหรับการรีเฟรชเพื่อดูเมตริกการสูญเสีย โปรดดูมากไปน้อยใน ML) ใช้เครื่องมือเพิ่มประสิทธิภาพ rmsprop
ที่มีอัตราการเรียนรู้อยู่ที่ 0.001 ในระหว่างการฝึกอบรม ให้ตรวจสอบความถูกต้องของการแยกประเภท
from tensorflow.keras.optimizers import RMSprop
model.compile(loss='binary_crossentropy',
optimizer=RMSprop(lr=0.001),
metrics=['acc'])
7 ฝึกโมเดลจากโปรแกรมสร้าง
ตั้งค่าโปรแกรมสร้างข้อมูลที่อ่านรูปภาพในโฟลเดอร์ต้นทาง แปลงรูปภาพเป็น Flo33 Tensor และให้ฟีด (ที่มีป้ายกํากับ) แก่เครือข่ายของคุณ
โดยคุณมีเครื่องกําเนิดไฟฟ้า 1 เครื่องสําหรับรูปภาพการฝึกอบรมและอีกเครื่องหนึ่งสําหรับรูปภาพการตรวจสอบ โปรแกรมสร้างจะผลตอบแทนกลุ่มรูปภาพขนาด 300x300 และป้ายกํากับ (ไบนารี)
ดังที่คุณอาจทราบอยู่แล้ว โดยปกติข้อมูลที่อยู่ในโครงข่ายระบบประสาทเทียมควรได้รับการปรับให้เป็นมาตรฐานเพื่อให้เครือข่ายสามารถประมวลผลข้อมูลได้อย่างมีประสิทธิภาพยิ่งขึ้น (ฟีดพิกเซลของ CNN กลายเป็นเรื่องแปลก) ในกรณีของคุณ คุณจะต้องประมวลผลรูปภาพล่วงหน้าด้วยการปรับค่าพิกเซลให้เป็นช่วง [0, 1] โดยอัตโนมัติ (เดิมค่าทั้งหมดอยู่ในช่วง [0, 255])
ใน Keras คุณจะดําเนินการผ่านคลาส keras.preprocessing.image.ImageDataGenerator
ได้โดยใช้พารามิเตอร์การปรับขนาด คลาส ImageDataGenerator
นี้ช่วยให้สร้างโปรแกรมสร้างชุดรูปภาพ Augmented Reality (และป้ายกํากับ) ผ่าน .flow(ข้อมูล ป้ายกํากับ) หรือ .flow_from_directory(ไดเรกทอรี) ได้ จากนั้นโปรแกรมสร้างเหล่านั้นจะใช้กับวิธีการของโมเดล Keras ที่ยอมรับโปรแกรมสร้างข้อมูลเป็นอินพุต: fit_generator
, evaluate_generator
และ predict_generator
from tensorflow.keras.preprocessing.image import ImageDataGenerator
# All images will be rescaled by 1./255
train_datagen = ImageDataGenerator(rescale=1./255)
# Flow training images in batches of 128 using train_datagen generator
train_generator = train_datagen.flow_from_directory(
'/tmp/horse-or-human/', # This is the source directory for training images
target_size=(300, 300), # All images will be resized to 150x150
batch_size=128,
# Since we use binary_crossentropy loss, we need binary labels
class_mode='binary')
8 เข้าฝึกอบรม
ฝึกสําหรับ 15 Epoch (อาจใช้เวลาสักครู่เพื่อทํางาน)
history = model.fit(
train_generator,
steps_per_epoch=8,
epochs=15,
verbose=1)
บันทึกค่าต่อ Epoch
การสูญเสียและความถูกต้องเป็นตัวบ่งชี้ความคืบหน้าในการฝึกอบรมที่ดีเยี่ยม เป็นการคาดเดาการแยกประเภทข้อมูลการฝึก จากนั้นจึงวัดกับป้ายกํากับที่รู้จักเพื่อคํานวณผลลัพธ์ ความแม่นยําเป็นส่วนหนึ่งของการคาดเดาที่ถูกต้อง
Epoch 1/15 9/9 [==============================] - 9s 1s/step - loss: 0.8662 - acc: 0.5151 Epoch 2/15 9/9 [==============================] - 8s 927ms/step - loss: 0.7212 - acc: 0.5969 Epoch 3/15 9/9 [==============================] - 8s 921ms/step - loss: 0.6612 - acc: 0.6592 Epoch 4/15 9/9 [==============================] - 8s 925ms/step - loss: 0.3135 - acc: 0.8481 Epoch 5/15 9/9 [==============================] - 8s 919ms/step - loss: 0.4640 - acc: 0.8530 Epoch 6/15 9/9 [==============================] - 8s 896ms/step - loss: 0.2306 - acc: 0.9231 Epoch 7/15 9/9 [==============================] - 8s 915ms/step - loss: 0.1464 - acc: 0.9396 Epoch 8/15 9/9 [==============================] - 8s 935ms/step - loss: 0.2663 - acc: 0.8919 Epoch 9/15 9/9 [==============================] - 8s 883ms/step - loss: 0.0772 - acc: 0.9698 Epoch 10/15 9/9 [==============================] - 9s 951ms/step - loss: 0.0403 - acc: 0.9805 Epoch 11/15 9/9 [==============================] - 8s 891ms/step - loss: 0.2618 - acc: 0.9075 Epoch 12/15 9/9 [==============================] - 8s 902ms/step - loss: 0.0434 - acc: 0.9873 Epoch 13/15 9/9 [==============================] - 8s 904ms/step - loss: 0.0187 - acc: 0.9932 Epoch 14/15 9/9 [==============================] - 9s 951ms/step - loss: 0.0974 - acc: 0.9649 Epoch 15/15 9/9 [==============================] - 8s 877ms/step - loss: 0.2859 - acc: 0.9338
9 ทดสอบโมเดล
ในคราวนี้ ให้เรียกใช้การคาดการณ์โดยใช้โมเดล โค้ดจะช่วยให้คุณเลือกไฟล์อย่างน้อย 1 ไฟล์จากระบบไฟล์ได้ จากนั้นจะอัปโหลดและเรียกใช้โมเดลดังกล่าวเพื่อระบุว่าเป็นวัตถุของม้าหรือมนุษย์
คุณสามารถดาวน์โหลดรูปภาพจากอินเทอร์เน็ตไปยังระบบไฟล์เพื่อลองใช้งานได้ โปรดทราบว่าเครือข่ายนี้อาจทําผิดพลาดจํานวนมากได้แม้ว่าความแม่นยําในการฝึกอบรมจะสูงกว่า 99% ก็ตาม
นั่นเป็นเพราะระบบเรียกว่าการโอเวอร์ฟิต ซึ่งหมายความว่าโครงข่ายระบบประสาทเทียมได้รับการฝึกพร้อมข้อมูลที่จํากัดมาก (มีรูปภาพแต่ละคลาสประมาณประมาณ 500 ภาพเท่านั้น) ดังนั้นการรู้จํารูปภาพที่ดูเหมือนอยู่ในชุดการฝึกจึงดีมาก แต่ภาพที่อยู่ในชุดการฝึกอบรมอาจไม่สําเร็จมากนัก
นั่นคือจุดข้อมูลที่พิสูจน์ว่ายิ่งคุณฝึกให้ข้อมูลมากเท่าใด เครือข่ายสุดท้ายของคุณก็จะยิ่งดีขึ้นเท่านั้น
มีเทคนิคมากมายที่นํามาใช้เพื่อทําให้การฝึกอบรมดีขึ้นได้ แม้จะมีข้อมูลจํากัด ซึ่งรวมถึงสิ่งที่เรียกว่าการเสริมรูปภาพ แต่นั่นอยู่นอกเหนือขอบเขตของ Codelab นี้
import numpy as np
from google.colab import files
from keras.preprocessing import image
uploaded = files.upload()
for fn in uploaded.keys():
# predicting images
path = '/content/' + fn
img = image.load_img(path, target_size=(300, 300))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
images = np.vstack([x])
classes = model.predict(images, batch_size=10)
print(classes[0])
if classes[0]>0.5:
print(fn + " is a human")
else:
print(fn + " is a horse")
เช่น สมมติว่าคุณต้องการทดสอบกับรูปภาพนี้
โคลัมเบียผลิตจากอะไรต่อไปนี้
ถึงจะเป็นกราฟิกการ์ตูน แต่ก็จัดประเภทได้ถูกต้อง
รูปภาพต่อไปนี้ยังจัดประเภทได้อย่างถูกต้องด้วย
ลองใช้ภาพของคุณเองและสํารวจ
10 แสดงภาพข้อมูลระดับกลาง
หากต้องการทําความเข้าใจเกี่ยวกับประเภทของฟีเจอร์ที่ CNN ได้เรียนรู้ สิ่งที่ควรทําอย่างน่าดึงดูดใจคือการแสดงภาพวิธีแปลงอินพุตขณะผ่าน CNN
เลือกรูปภาพแบบสุ่มจากชุดการฝึก แล้วสร้างตัวเลขที่แต่ละแถวเป็นเอาต์พุตของเลเยอร์และรูปภาพแต่ละรูปในแถวเป็นตัวกรองที่เฉพาะเจาะจงในแผนที่ฟีเจอร์เอาต์พุตนั้นๆ เรียกใช้เซลล์นั้นอีกครั้งเพื่อสร้างการนําเสนอระดับกลางสําหรับรูปภาพการฝึกอบรมที่หลากหลาย
import numpy as np
import random
from tensorflow.keras.preprocessing.image import img_to_array, load_img
# Let's define a new Model that will take an image as input, and will output
# intermediate representations for all layers in the previous model after
# the first.
successive_outputs = [layer.output for layer in model.layers[1:]]
#visualization_model = Model(img_input, successive_outputs)
visualization_model = tf.keras.models.Model(inputs = model.input, outputs = successive_outputs)
# Let's prepare a random input image from the training set.
horse_img_files = [os.path.join(train_horse_dir, f) for f in train_horse_names]
human_img_files = [os.path.join(train_human_dir, f) for f in train_human_names]
img_path = random.choice(horse_img_files + human_img_files)
img = load_img(img_path, target_size=(300, 300)) # this is a PIL image
x = img_to_array(img) # Numpy array with shape (150, 150, 3)
x = x.reshape((1,) + x.shape) # Numpy array with shape (1, 150, 150, 3)
# Rescale by 1/255
x /= 255
# Let's run our image through our network, thus obtaining all
# intermediate representations for this image.
successive_feature_maps = visualization_model.predict(x)
# These are the names of the layers, so can have them as part of our plot
layer_names = [layer.name for layer in model.layers]
# Now let's display our representations
for layer_name, feature_map in zip(layer_names, successive_feature_maps):
if len(feature_map.shape) == 4:
# Just do this for the conv / maxpool layers, not the fully-connected layers
n_features = feature_map.shape[-1] # number of features in feature map
# The feature map has shape (1, size, size, n_features)
size = feature_map.shape[1]
# We will tile our images in this matrix
display_grid = np.zeros((size, size * n_features))
for i in range(n_features):
# Postprocess the feature to make it visually palatable
x = feature_map[0, :, :, i]
x -= x.mean()
if x.std()>0:
x /= x.std()
x *= 64
x += 128
x = np.clip(x, 0, 255).astype('uint8')
# We'll tile each filter into this big horizontal grid
display_grid[:, i * size : (i + 1) * size] = x
# Display the grid
scale = 20. / n_features
plt.figure(figsize=(scale * n_features, scale))
plt.title(layer_name)
plt.grid(False)
plt.imshow(display_grid, aspect='auto', cmap='viridis')
ตัวอย่างผลลัพธ์มีดังนี้
ดังที่คุณเห็น คุณเปลี่ยนจากพิกเซลดิบของรูปภาพเป็นการนําเสนอนามธรรมและกะทัดรัดได้มากยิ่งขึ้น การเป็นตัวแทนในดาวน์สตรีมจะเริ่มไฮไลต์สิ่งที่เครือข่ายให้ความสนใจ และนําเสนอฟีเจอร์น้อยลงน้อยลงเมื่อ "activated." ซึ่งส่วนใหญ่ตั้งค่าไว้เป็น 0 หรือเรียกกันว่า sparsity ความพร้อมรับมือกับปัญหาเป็นฟีเจอร์หลักของการเรียนรู้อย่างลึกซึ้ง
ข้อมูลที่นําเสนอเหล่านี้มีข้อมูลเกี่ยวกับพิกเซลต้นฉบับของรูปภาพน้อยลง แต่เป็นข้อมูลที่ละเอียดยิ่งขึ้นเกี่ยวกับคลาสของรูปภาพ คุณอาจคิดว่า CNN (หรือเครือข่ายทั่วไปโดยทั่วไป) เป็นไปป์ไลน์การกลั่นข้อมูล
11 ยินดีด้วย
คุณได้เรียนรู้การใช้ CNN เพื่อเพิ่มประสิทธิภาพรูปภาพที่ซับซ้อน หากต้องการดูวิธีเพิ่มประสิทธิภาพโมเดลคอมพิวเตอร์วิทัศน์ของคุณ ให้ไปที่ใช้โครงข่ายระบบประสาทเทียม (CNN) ด้วยชุดข้อมูลขนาดใหญ่เพื่อหลีกเลี่ยงการสูญเสียพื้นที่มากเกินไป