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)

ด้วยความช่วยเหลือของโค้ดด้านบนเราสามารถปรับปรุงฟังก์ชั่นเพื่อรวมชุดการแปลงรูปภาพดังนั้นเมื่อเราจะฝึกอบรมเราสามารถครอบตัดรูปภาพแบบสุ่มดังนั้นเราจึงได้รูปแบบต่างๆของรูปภาพมากขึ้น