CNTK - Convolutional Neural Network
ในบทนี้ให้เราศึกษาวิธีสร้าง Convolutional Neural Network (CNN) ใน CNTK
บทนำ
Convolutional Neural Network (CNN) ประกอบด้วยเซลล์ประสาทซึ่งมีน้ำหนักและอคติที่เรียนรู้ได้ ด้วยเหตุนี้จึงเป็นเหมือนโครงข่ายประสาทเทียมทั่วไป (NNs)
ถ้าเราจำการทำงานของ NN ธรรมดาเซลล์ประสาททุกตัวจะได้รับอินพุตตั้งแต่หนึ่งตัวขึ้นไปรับผลรวมถ่วงน้ำหนักและส่งผ่านฟังก์ชันกระตุ้นเพื่อสร้างผลลัพธ์สุดท้าย ที่นี่คำถามเกิดขึ้นว่าถ้า CNN และ NN ธรรมดามีความคล้ายคลึงกันมากมายอะไรที่ทำให้เครือข่ายทั้งสองนี้แตกต่างกัน?
สิ่งที่ทำให้แตกต่างกันคือการจัดการข้อมูลอินพุตและประเภทของเลเยอร์ โครงสร้างของข้อมูลอินพุตจะถูกละเว้นใน NN ธรรมดาและข้อมูลทั้งหมดจะถูกแปลงเป็นอาร์เรย์ 1-D ก่อนที่จะป้อนเข้าสู่เครือข่าย
แต่สถาปัตยกรรม Convolutional Neural Network สามารถพิจารณาโครงสร้าง 2 มิติของภาพประมวลผลและอนุญาตให้ดึงคุณสมบัติที่เฉพาะเจาะจงสำหรับรูปภาพได้ นอกจากนี้ CNN ยังมีข้อได้เปรียบในการมีเลเยอร์ Convolutional อย่างน้อยหนึ่งเลเยอร์และเลเยอร์พูลซึ่งเป็นส่วนประกอบหลักของ CNN
เลเยอร์เหล่านี้ตามด้วยเลเยอร์ที่เชื่อมต่ออย่างสมบูรณ์อย่างน้อยหนึ่งเลเยอร์เช่นเดียวกับใน NN หลายชั้นมาตรฐาน ดังนั้นเราจึงสามารถนึกถึง CNN เป็นกรณีพิเศษของเครือข่ายที่เชื่อมต่ออย่างสมบูรณ์
สถาปัตยกรรม Convolutional Neural Network (CNN)
สถาปัตยกรรมของ CNN โดยพื้นฐานแล้วเป็นรายการของเลเยอร์ที่เปลี่ยน 3 มิติ ได้แก่ ความกว้างความสูงและความลึกของปริมาณภาพให้เป็นปริมาณเอาต์พุต 3 มิติ ประเด็นสำคัญอย่างหนึ่งที่ควรทราบก็คือเซลล์ประสาททุกเซลล์ในชั้นปัจจุบันจะเชื่อมต่อกับส่วนเล็ก ๆ ของเอาต์พุตจากเลเยอร์ก่อนหน้าซึ่งเหมือนกับการซ้อนทับฟิลเตอร์ N * N บนภาพอินพุต
ใช้ฟิลเตอร์ M ซึ่งโดยทั่วไปแล้วตัวแยกคุณสมบัติที่ดึงคุณสมบัติเช่นขอบมุมและอื่น ๆ ต่อไปนี้เป็นเลเยอร์ [INPUT-CONV-RELU-POOL-FC] ที่ใช้ในการสร้าง Convolutional neural networks (CNNs) -
INPUT- ตามความหมายของชื่อเลเยอร์นี้จะเก็บค่าพิกเซลดิบ ค่าพิกเซลดิบหมายถึงข้อมูลของรูปภาพตามที่เป็นจริง ตัวอย่าง INPUT [64 × 64 × 3] คือภาพ RGB 3 ช่องที่มีความกว้าง -64, สูง -64 และความลึก -3
CONV- เลเยอร์นี้เป็นหนึ่งในองค์ประกอบพื้นฐานของ CNN เนื่องจากการคำนวณส่วนใหญ่เสร็จสิ้นในเลเยอร์นี้ ตัวอย่าง - ถ้าเราใช้ 6 ฟิลเตอร์กับ INPUT ที่กล่าวมาข้างต้น [64 × 64 × 3] อาจส่งผลให้โวลุ่ม [64 × 64 × 6]
RELU− เรียกอีกอย่างว่าเลเยอร์หน่วยเชิงเส้นแก้ไขซึ่งใช้ฟังก์ชันการเปิดใช้งานกับเอาต์พุตของเลเยอร์ก่อนหน้า ในลักษณะอื่น RELU จะเพิ่มความไม่เป็นเชิงเส้นลงในเครือข่าย
POOL- เลเยอร์นี้เช่น Pooling layer เป็นกลุ่มอาคารอื่นของ CNN งานหลักของเลเยอร์นี้คือการสุ่มตัวอย่างแบบลงซึ่งหมายความว่าจะทำงานอย่างอิสระในทุกส่วนของอินพุตและปรับขนาดเชิงพื้นที่
FC- เรียกว่าเลเยอร์ที่เชื่อมต่ออย่างเต็มที่หรือโดยเฉพาะเลเยอร์เอาต์พุต ใช้ในการคำนวณคะแนนคลาสเอาต์พุตและผลลัพธ์ที่ได้คือปริมาตรขนาด 1 * 1 * Lโดย L คือตัวเลขที่ตรงกับคะแนนชั้นเรียน
แผนภาพด้านล่างแสดงถึงสถาปัตยกรรมทั่วไปของ CNNs−
การสร้างโครงสร้าง CNN
เราได้เห็นสถาปัตยกรรมและพื้นฐานของ CNN แล้วตอนนี้เรากำลังจะสร้างเครือข่าย Convolutional โดยใช้ CNTK ที่นี่ก่อนอื่นเราจะดูวิธีการรวมโครงสร้างของ CNN จากนั้นเราจะดูวิธีฝึกพารามิเตอร์ของมัน
ในที่สุดเราจะมาดูกันว่าเราจะปรับปรุงโครงข่ายประสาทเทียมได้อย่างไรโดยการเปลี่ยนโครงสร้างด้วยการตั้งค่าเลเยอร์ต่างๆ เราจะใช้ชุดข้อมูลภาพ MNIST
ก่อนอื่นมาสร้างโครงสร้าง CNN โดยทั่วไปเมื่อเราสร้าง CNN เพื่อจดจำรูปแบบในรูปภาพเราจะทำสิ่งต่อไปนี้
เราใช้การรวมกันของเลเยอร์การแปลงและการรวมกัน
เลเยอร์ที่ซ่อนอยู่อย่างน้อยหนึ่งชั้นที่ส่วนท้ายของเครือข่าย
ในที่สุดเราก็เสร็จสิ้นเครือข่ายด้วยชั้น softmax เพื่อการจำแนกประเภท
ด้วยความช่วยเหลือของขั้นตอนต่อไปนี้เราสามารถสร้างโครงสร้างเครือข่าย
Step 1- ขั้นแรกเราต้องนำเข้าเลเยอร์ที่จำเป็นสำหรับ CNN
from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling
Step 2- ต่อไปเราต้องนำเข้าฟังก์ชันการเปิดใช้งานสำหรับ CNN
from cntk.ops import log_softmax, relu
Step 3- หลังจากนั้นในการเริ่มต้นเลเยอร์คอนโวลูชั่นในภายหลังเราจำเป็นต้องนำเข้าไฟล์ glorot_uniform_initializer ดังต่อไปนี้
from cntk.initializer import glorot_uniform
Step 4- ถัดไปในการสร้างตัวแปรอินพุตนำเข้าไฟล์ input_variableฟังก์ชัน และนำเข้าdefault_option เพื่อให้การกำหนดค่า NN ง่ายขึ้นเล็กน้อย
from cntk import input_variable, default_options
Step 5- ตอนนี้ในการจัดเก็บภาพอินพุตสร้างไฟล์ input_variable. มันจะมีสามช่องคือสีแดงสีเขียวและสีน้ำเงิน จะมีขนาด 28 x 28 พิกเซล
features = input_variable((3,28,28))
Step 6− ต่อไปเราต้องสร้างใหม่ input_variable เพื่อจัดเก็บป้ายกำกับเพื่อทำนาย
labels = input_variable(10)
Step 7- ตอนนี้เราต้องสร้างไฟล์ default_optionสำหรับ NN และเราจำเป็นต้องใช้ไฟล์glorot_uniform เป็นฟังก์ชันเริ่มต้น
with default_options(initialization=glorot_uniform, activation=relu):
Step 8- ถัดไปในการกำหนดโครงสร้างของ NN เราต้องสร้างไฟล์ Sequential ชุดชั้น
Step 9- ตอนนี้เราต้องเพิ่มไฟล์ Convolutional2D เลเยอร์ด้วย filter_shape ของ 5 และ a strides การตั้งค่าของ 1, ภายใน Sequentialชุดชั้น นอกจากนี้ให้เปิดใช้งานช่องว่างภายในเพื่อให้ภาพมีการปรับขนาดเพื่อคงขนาดเดิม
model = Sequential([
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
Step 10- ตอนนี้ได้เวลาเพิ่มไฟล์ MaxPooling เลเยอร์ด้วย filter_shape จาก 2 และก strides การตั้งค่า 2 เพื่อบีบอัดภาพลงครึ่งหนึ่ง
MaxPooling(filter_shape=(2,2), strides=(2,2)),
Step 11- ตอนนี้ตามที่เราทำในขั้นตอนที่ 9 เราต้องเพิ่มอีก Convolutional2D เลเยอร์ด้วย filter_shape ของ 5 และ a stridesการตั้งค่า 1 ใช้ 16 ตัวกรอง นอกจากนี้ให้เปิดใช้งานช่องว่างภายในเพื่อให้ขนาดของรูปภาพที่สร้างโดยเลเยอร์การรวมกลุ่มก่อนหน้านี้ควรคงไว้
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
Step 12- ตอนนี้ตามที่เราทำในขั้นตอนที่ 10 ให้เพิ่มอีกอัน MaxPooling เลเยอร์ด้วย filter_shape ของ 3 และ a strides การตั้งค่า 3 เพื่อลดภาพเป็นหนึ่งในสาม
MaxPooling(filter_shape=(3,3), strides=(3,3)),
Step 13- ในที่สุดเพิ่มเลเยอร์หนาแน่นด้วยเซลล์ประสาท 10 เซลล์สำหรับ 10 คลาสที่เป็นไปได้เครือข่ายสามารถทำนายได้ ในการเปลี่ยนเครือข่ายให้เป็นรูปแบบการจำแนกประเภทให้ใช้ไฟล์log_siftmax ฟังก์ชั่นการเปิดใช้งาน
Dense(10, activation=log_softmax)
])
ตัวอย่างที่สมบูรณ์สำหรับการสร้างโครงสร้าง CNN
from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling
from cntk.ops import log_softmax, relu
from cntk.initializer import glorot_uniform
from cntk import input_variable, default_options
features = input_variable((3,28,28))
labels = input_variable(10)
with default_options(initialization=glorot_uniform, activation=relu):
model = Sequential([
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
MaxPooling(filter_shape=(2,2), strides=(2,2)),
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
MaxPooling(filter_shape=(3,3), strides=(3,3)),
Dense(10, activation=log_softmax)
])
z = model(features)
ฝึกอบรม CNN ด้วยภาพ
เมื่อเราสร้างโครงสร้างของเครือข่ายแล้วก็ถึงเวลาฝึกอบรมเครือข่าย แต่ก่อนที่จะเริ่มการฝึกอบรมเครือข่ายของเราเราจำเป็นต้องตั้งค่าแหล่งที่มาของมินิแบทช์เนื่องจากการฝึก NN ที่ทำงานกับรูปภาพต้องใช้หน่วยความจำมากกว่าที่คอมพิวเตอร์ส่วนใหญ่มี
เราได้สร้างแหล่งที่มาของมินิแบทช์แล้วในส่วนก่อนหน้านี้ ต่อไปนี้เป็นรหัส Python เพื่อตั้งค่ามินิแบทช์สองแหล่ง -
อย่างที่เรามี create_datasource ขณะนี้เราสามารถสร้างแหล่งข้อมูลแยกกันสองแหล่ง (การฝึกอบรมและการทดสอบหนึ่ง) เพื่อฝึกโมเดล
train_datasource = create_datasource('mnist_train')
test_datasource = create_datasource('mnist_test', max_sweeps=1, train=False)
ตอนนี้เมื่อเราเตรียมภาพแล้วเราสามารถเริ่มฝึก NN ของเราได้ ดังที่เราทำในส่วนก่อนหน้านี้เราสามารถใช้วิธีการฝึกในฟังก์ชันการสูญเสียเพื่อเริ่มการฝึกอบรม ต่อไปนี้เป็นรหัสสำหรับสิ่งนี้ -
from cntk import Function
from cntk.losses import cross_entropy_with_softmax
from cntk.metrics import classification_error
from cntk.learners import sgd
@Function
def criterion_factory(output, targets):
loss = cross_entropy_with_softmax(output, targets)
metric = classification_error(output, targets)
return loss, metric
loss = criterion_factory(z, labels)
learner = sgd(z.parameters, lr=0.2)
ด้วยความช่วยเหลือของรหัสก่อนหน้านี้เราได้ตั้งค่าการสูญเสียและผู้เรียนสำหรับ NN รหัสต่อไปนี้จะฝึกและตรวจสอบความถูกต้องของ NN−
from cntk.logging import ProgressPrinter
from cntk.train import TestConfig
progress_writer = ProgressPrinter(0)
test_config = TestConfig(test_datasource)
input_map = {
features: train_datasource.streams.features,
labels: train_datasource.streams.labels
}
loss.train(train_datasource,
max_epochs=10,
minibatch_size=64,
epoch_size=60000,
parameter_learners=[learner],
model_inputs_to_streams=input_map,
callbacks=[progress_writer, test_config])
ตัวอย่างการใช้งานที่สมบูรณ์
from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling
from cntk.ops import log_softmax, relu
from cntk.initializer import glorot_uniform
from cntk import input_variable, default_options
features = input_variable((3,28,28))
labels = input_variable(10)
with default_options(initialization=glorot_uniform, activation=relu):
model = Sequential([
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
MaxPooling(filter_shape=(2,2), strides=(2,2)),
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
MaxPooling(filter_shape=(3,3), strides=(3,3)),
Dense(10, activation=log_softmax)
])
z = model(features)
import os
from cntk.io import MinibatchSource, StreamDef, StreamDefs, ImageDeserializer, INFINITELY_REPEAT
import cntk.io.transforms as xforms
def create_datasource(folder, train=True, max_sweeps=INFINITELY_REPEAT):
mapping_file = os.path.join(folder, 'mapping.bin')
image_transforms = []
if train:
image_transforms += [
xforms.crop(crop_type='randomside', side_ratio=0.8),
xforms.scale(width=28, height=28, channels=3, interpolations='linear')
]
stream_definitions = StreamDefs(
features=StreamDef(field='image', transforms=image_transforms),
labels=StreamDef(field='label', shape=10)
)
deserializer = ImageDeserializer(mapping_file, stream_definitions)
return MinibatchSource(deserializer, max_sweeps=max_sweeps)
train_datasource = create_datasource('mnist_train')
test_datasource = create_datasource('mnist_test', max_sweeps=1, train=False)
from cntk import Function
from cntk.losses import cross_entropy_with_softmax
from cntk.metrics import classification_error
from cntk.learners import sgd
@Function
def criterion_factory(output, targets):
loss = cross_entropy_with_softmax(output, targets)
metric = classification_error(output, targets)
return loss, metric
loss = criterion_factory(z, labels)
learner = sgd(z.parameters, lr=0.2)
from cntk.logging import ProgressPrinter
from cntk.train import TestConfig
progress_writer = ProgressPrinter(0)
test_config = TestConfig(test_datasource)
input_map = {
features: train_datasource.streams.features,
labels: train_datasource.streams.labels
}
loss.train(train_datasource,
max_epochs=10,
minibatch_size=64,
epoch_size=60000,
parameter_learners=[learner],
model_inputs_to_streams=input_map,
callbacks=[progress_writer, test_config])
เอาต์พุต
-------------------------------------------------------------------
average since average since examples
loss last metric last
------------------------------------------------------
Learning rate per minibatch: 0.2
142 142 0.922 0.922 64
1.35e+06 1.51e+07 0.896 0.883 192
[………]
การแปลงภาพ
ดังที่เราได้เห็นแล้วการฝึก NN ที่ใช้ในการจดจำภาพเป็นเรื่องยากและต้องใช้ข้อมูลจำนวนมากเพื่อฝึกอบรมด้วย อีกปัญหาหนึ่งคือพวกเขามักจะใส่รูปภาพที่ใช้ระหว่างการฝึกมากเกินไป ให้เราดูตัวอย่างเมื่อเรามีรูปถ่ายใบหน้าในตำแหน่งตั้งตรงนางแบบของเราจะมีปัญหาในการจดจำใบหน้าที่หมุนไปในทิศทางอื่น
เพื่อเอาชนะปัญหาดังกล่าวเราสามารถใช้การเพิ่มรูปภาพและ CNTK รองรับการแปลงเฉพาะเมื่อสร้างแหล่งที่มาของมินิแบทช์สำหรับรูปภาพ เราสามารถใช้การแปลงได้หลายรูปแบบดังนี้
เราสามารถสุ่มครอบตัดรูปภาพที่ใช้ในการฝึกอบรมโดยใช้โค้ดเพียงไม่กี่บรรทัด
เราสามารถใช้สเกลและสีได้ด้วย
มาดูความช่วยเหลือของโค้ด Python ต่อไปนี้ว่าเราจะเปลี่ยนรายการการเปลี่ยนแปลงได้อย่างไรโดยรวมการแปลงการปลูกพืชไว้ในฟังก์ชันที่ใช้ในการสร้างแหล่งมินิแบทช์ก่อนหน้านี้
import os
from cntk.io import MinibatchSource, StreamDef, StreamDefs, ImageDeserializer, INFINITELY_REPEAT
import cntk.io.transforms as xforms
def create_datasource(folder, train=True, max_sweeps=INFINITELY_REPEAT):
mapping_file = os.path.join(folder, 'mapping.bin')
image_transforms = []
if train:
image_transforms += [
xforms.crop(crop_type='randomside', side_ratio=0.8),
xforms.scale(width=28, height=28, channels=3, interpolations='linear')
]
stream_definitions = StreamDefs(
features=StreamDef(field='image', transforms=image_transforms),
labels=StreamDef(field='label', shape=10)
)
deserializer = ImageDeserializer(mapping_file, stream_definitions)
return MinibatchSource(deserializer, max_sweeps=max_sweeps)
ด้วยความช่วยเหลือของโค้ดด้านบนเราสามารถปรับปรุงฟังก์ชั่นเพื่อรวมชุดการแปลงรูปภาพดังนั้นเมื่อเราจะฝึกอบรมเราสามารถครอบตัดรูปภาพแบบสุ่มดังนั้นเราจึงได้รูปแบบต่างๆของรูปภาพมากขึ้น