![]() | ![]() | ![]() |
คู่มือนี้จะแนะนำ Swift for TensorFlow โดยการสร้างโมเดลการเรียนรู้ของเครื่องที่จัดหมวดหมู่ดอกไอริสตามสายพันธุ์ ใช้ Swift สำหรับ TensorFlow เพื่อ:
- สร้างแบบจำลอง,
- ฝึกโมเดลนี้กับข้อมูลตัวอย่างและ
- ใช้แบบจำลองเพื่อคาดการณ์ข้อมูลที่ไม่รู้จัก
การเขียนโปรแกรม TensorFlow
คู่มือนี้ใช้แนวคิด Swift ระดับสูงสำหรับ TensorFlow:
- นำเข้าข้อมูลด้วย Epochs API
- สร้างแบบจำลองโดยใช้ Swift abstractions
- ใช้ไลบรารี Python โดยใช้การทำงานร่วมกันของ Python ของ Swift เมื่อไม่มีไลบรารี Swift แท้
บทช่วยสอนนี้มีโครงสร้างเหมือนกับโปรแกรม TensorFlow หลายโปรแกรม:
- นำเข้าและแยกวิเคราะห์ชุดข้อมูล
- เลือกประเภทของรุ่น
- ฝึกโมเดล.
- ประเมินประสิทธิภาพของแบบจำลอง
- ใช้แบบจำลองที่ได้รับการฝึกอบรมมาในการทำนาย
โปรแกรมติดตั้ง
กำหนดค่าการนำเข้า
นำเข้า TensorFlow และโมดูล Python ที่มีประโยชน์
import TensorFlow
import PythonKit
// This cell is here to display the plots in a Jupyter Notebook.
// Do not copy it into another environment.
%include "EnableIPythonDisplay.swift"
print(IPythonDisplay.shell.enable_matplotlib("inline"))
('inline', 'module://ipykernel.pylab.backend_inline')
let plt = Python.import("matplotlib.pyplot")
import Foundation
import FoundationNetworking
func download(from sourceString: String, to destinationString: String) {
let source = URL(string: sourceString)!
let destination = URL(fileURLWithPath: destinationString)
let data = try! Data.init(contentsOf: source)
try! data.write(to: destination)
}
ปัญหาการจำแนกม่านตา
ลองนึกภาพคุณเป็นนักพฤกษศาสตร์ที่กำลังมองหาวิธีอัตโนมัติในการจัดหมวดหมู่ดอกไอริสแต่ละดอกที่คุณพบ แมชชีนเลิร์นนิงมีอัลกอริธึมมากมายในการจำแนกดอกไม้ตามสถิติ ตัวอย่างเช่น โปรแกรมแมชชีนเลิร์นนิงที่ซับซ้อนสามารถจำแนกดอกไม้ตามรูปถ่ายได้ ความทะเยอทะยานของเราค่อนข้างเรียบง่าย เราจะจำแนกดอกไอริสตามความยาวและความกว้างของ กลีบเลี้ยง และ กลีบดอก
สกุล Iris มีประมาณ 300 สปีชีส์ แต่โปรแกรมของเราจะจำแนกประเภทต่อไปนี้เท่านั้น:
- ไอริส เซโตซ่า
- Iris virginica
- ไอริส versicolor
![]() |
รูปที่ 1 Iris setosa (โดย Radomil , CC BY-SA 3.0 ), Iris versicolor , (โดย Dlanglois , CC BY-SA 3.0) และ Iris virginica (โดย Frank Mayfield , CC BY-SA 2.0) |
โชคดีที่มีคนสร้าง ชุดข้อมูลดอกไอริส 120 ดอก พร้อมขนาดกลีบเลี้ยงและกลีบดอกแล้ว นี่คือชุดข้อมูลคลาสสิกที่เป็นที่นิยมสำหรับปัญหาการจำแนกประเภทการเรียนรู้ของเครื่องสำหรับผู้เริ่มต้น
นำเข้าและแยกวิเคราะห์ชุดข้อมูลการฝึกอบรม
ดาวน์โหลดไฟล์ชุดข้อมูลและแปลงเป็นโครงสร้างที่โปรแกรม Swift นี้สามารถใช้ได้
ดาวน์โหลดชุดข้อมูล
ดาวน์โหลดไฟล์ชุดข้อมูลการฝึกอบรมจาก http://download.tensorflow.org/data/iris_training.csv
let trainDataFilename = "iris_training.csv"
download(from: "http://download.tensorflow.org/data/iris_training.csv", to: trainDataFilename)
ตรวจสอบข้อมูล
ชุดข้อมูลนี้ iris_training.csv
เป็นไฟล์ข้อความธรรมดาที่จัดเก็บข้อมูลแบบตารางซึ่งจัดรูปแบบเป็นค่าที่คั่นด้วยเครื่องหมายจุลภาค (CSV) มาดู 5 รายการแรกกัน
let f = Python.open(trainDataFilename)
for _ in 0..<5 {
print(Python.next(f).strip())
}
print(f.close())
120,4,setosa,versicolor,virginica 6.4,2.8,5.6,2.2,2 5.0,2.3,3.3,1.0,1 4.9,2.5,4.5,1.7,2 4.9,3.1,1.5,0.1,0 None
จากมุมมองของชุดข้อมูลนี้ ให้สังเกตสิ่งต่อไปนี้:
- บรรทัดแรกคือส่วนหัวที่มีข้อมูลเกี่ยวกับชุดข้อมูล:
- มีตัวอย่างทั้งหมด 120 ตัวอย่าง แต่ละตัวอย่างมีสี่คุณลักษณะและหนึ่งในสามชื่อป้ายกำกับที่เป็นไปได้
- แถวที่ตามมาคือบันทึกข้อมูล หนึ่ง ตัวอย่าง ต่อบรรทัด โดยที่:
ลองเขียนมันออกมาเป็นรหัส:
let featureNames = ["sepal_length", "sepal_width", "petal_length", "petal_width"]
let labelName = "species"
let columnNames = featureNames + [labelName]
print("Features: \(featureNames)")
print("Label: \(labelName)")
Features: ["sepal_length", "sepal_width", "petal_length", "petal_width"] Label: species
ป้ายกำกับแต่ละป้ายเชื่อมโยงกับชื่อสตริง (เช่น "setosa") แต่การเรียนรู้ของเครื่องมักอาศัยค่าตัวเลข หมายเลขฉลากถูกแมปกับการแสดงชื่อ เช่น:
-
0
: ไอริส เซโตซ่า -
1
: ไอริส versicolor -
2
: Iris virginica
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับคุณลักษณะและป้ายกำกับ โปรดดู ส่วนคำศัพท์ ML ของ Machine Learning Crash Course
let classNames = ["Iris setosa", "Iris versicolor", "Iris virginica"]
สร้างชุดข้อมูลโดยใช้ Epochs API
Epochs API ของ Swift for TensorFlow เป็น API ระดับสูงสำหรับการอ่านข้อมูลและแปลงเป็นรูปแบบที่ใช้สำหรับการฝึกอบรม
let batchSize = 32
/// A batch of examples from the iris dataset.
struct IrisBatch {
/// [batchSize, featureCount] tensor of features.
let features: Tensor<Float>
/// [batchSize] tensor of labels.
let labels: Tensor<Int32>
}
/// Conform `IrisBatch` to `Collatable` so that we can load it into a `TrainingEpoch`.
extension IrisBatch: Collatable {
public init<BatchSamples: Collection>(collating samples: BatchSamples)
where BatchSamples.Element == Self {
/// `IrisBatch`es are collated by stacking their feature and label tensors
/// along the batch axis to produce a single feature and label tensor
features = Tensor<Float>(stacking: samples.map{$0.features})
labels = Tensor<Int32>(stacking: samples.map{$0.labels})
}
}
เนื่องจากชุดข้อมูลที่เราดาวน์โหลดมาอยู่ในรูปแบบ CSV ให้เขียนฟังก์ชันเพื่อโหลดข้อมูลเป็นรายการของวัตถุ IrisBatch
/// Initialize an `IrisBatch` dataset from a CSV file.
func loadIrisDatasetFromCSV(
contentsOf: String, hasHeader: Bool, featureColumns: [Int], labelColumns: [Int]) -> [IrisBatch] {
let np = Python.import("numpy")
let featuresNp = np.loadtxt(
contentsOf,
delimiter: ",",
skiprows: hasHeader ? 1 : 0,
usecols: featureColumns,
dtype: Float.numpyScalarTypes.first!)
guard let featuresTensor = Tensor<Float>(numpy: featuresNp) else {
// This should never happen, because we construct featuresNp in such a
// way that it should be convertible to tensor.
fatalError("np.loadtxt result can't be converted to Tensor")
}
let labelsNp = np.loadtxt(
contentsOf,
delimiter: ",",
skiprows: hasHeader ? 1 : 0,
usecols: labelColumns,
dtype: Int32.numpyScalarTypes.first!)
guard let labelsTensor = Tensor<Int32>(numpy: labelsNp) else {
// This should never happen, because we construct labelsNp in such a
// way that it should be convertible to tensor.
fatalError("np.loadtxt result can't be converted to Tensor")
}
return zip(featuresTensor.unstacked(), labelsTensor.unstacked()).map{IrisBatch(features: $0.0, labels: $0.1)}
}
ตอนนี้เราสามารถใช้ฟังก์ชันการโหลด CSV เพื่อโหลดชุดข้อมูลการฝึกและสร้างอ็อบเจกต์ TrainingEpochs
let trainingDataset: [IrisBatch] = loadIrisDatasetFromCSV(contentsOf: trainDataFilename,
hasHeader: true,
featureColumns: [0, 1, 2, 3],
labelColumns: [4])
let trainingEpochs: TrainingEpochs = TrainingEpochs(samples: trainingDataset, batchSize: batchSize)
ออบเจ็กต์ TrainingEpochs
เป็นลำดับอนันต์ของยุค แต่ละยุคประกอบด้วย IrisBatch
es มาดูองค์ประกอบแรกของยุคแรกกัน
let firstTrainEpoch = trainingEpochs.next()!
let firstTrainBatch = firstTrainEpoch.first!.collated
let firstTrainFeatures = firstTrainBatch.features
let firstTrainLabels = firstTrainBatch.labels
print("First batch of features: \(firstTrainFeatures)")
print("firstTrainFeatures.shape: \(firstTrainFeatures.shape)")
print("First batch of labels: \(firstTrainLabels)")
print("firstTrainLabels.shape: \(firstTrainLabels.shape)")
First batch of features: [[5.1, 2.5, 3.0, 1.1], [6.4, 3.2, 4.5, 1.5], [4.9, 3.1, 1.5, 0.1], [5.0, 2.0, 3.5, 1.0], [6.3, 2.5, 5.0, 1.9], [6.7, 3.1, 5.6, 2.4], [4.9, 3.1, 1.5, 0.1], [7.7, 2.8, 6.7, 2.0], [6.7, 3.0, 5.0, 1.7], [7.2, 3.6, 6.1, 2.5], [4.8, 3.0, 1.4, 0.1], [5.2, 3.4, 1.4, 0.2], [5.0, 3.5, 1.3, 0.3], [4.9, 3.1, 1.5, 0.1], [5.0, 3.5, 1.6, 0.6], [6.7, 3.3, 5.7, 2.1], [7.7, 3.8, 6.7, 2.2], [6.2, 3.4, 5.4, 2.3], [4.8, 3.4, 1.6, 0.2], [6.0, 2.9, 4.5, 1.5], [5.0, 3.0, 1.6, 0.2], [6.3, 3.4, 5.6, 2.4], [5.1, 3.8, 1.9, 0.4], [4.8, 3.1, 1.6, 0.2], [7.6, 3.0, 6.6, 2.1], [5.7, 3.0, 4.2, 1.2], [6.3, 3.3, 6.0, 2.5], [5.6, 2.5, 3.9, 1.1], [5.0, 3.4, 1.6, 0.4], [6.1, 3.0, 4.9, 1.8], [5.0, 3.3, 1.4, 0.2], [6.3, 3.3, 4.7, 1.6]] firstTrainFeatures.shape: [32, 4] First batch of labels: [1, 1, 0, 1, 2, 2, 0, 2, 1, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 1, 0, 2, 0, 0, 2, 1, 2, 1, 0, 2, 0, 1] firstTrainLabels.shape: [32]
โปรดสังเกตว่าคุณลักษณะสำหรับตัวอย่าง batchSize
แรกถูกจัดกลุ่มเข้าด้วยกัน (หรือ batched ) เป็น firstTrainFeatures
และป้ายกำกับสำหรับตัวอย่าง batchSize
แรกจะถูกจัดกลุ่มเป็น firstTrainLabels
คุณสามารถเริ่มเห็นบางคลัสเตอร์โดยพล็อตคุณสมบัติบางอย่างจากแบตช์ โดยใช้ matplotlib ของ Python:
let firstTrainFeaturesTransposed = firstTrainFeatures.transposed()
let petalLengths = firstTrainFeaturesTransposed[2].scalars
let sepalLengths = firstTrainFeaturesTransposed[0].scalars
plt.scatter(petalLengths, sepalLengths, c: firstTrainLabels.array.scalars)
plt.xlabel("Petal length")
plt.ylabel("Sepal length")
plt.show()
Use `print()` to show values.
เลือกประเภทรุ่น
ทำไมต้องรุ่น?
โมเดล คือความสัมพันธ์ระหว่างคุณลักษณะและป้ายกำกับ สำหรับปัญหาการจำแนกม่านตา แบบจำลองกำหนดความสัมพันธ์ระหว่างการวัดกลีบเลี้ยงและกลีบดอกกับชนิดของม่านตาที่คาดการณ์ไว้ โมเดลอย่างง่ายบางตัวสามารถอธิบายได้ด้วยพีชคณิตสองสามบรรทัด แต่โมเดลการเรียนรู้ของเครื่องที่ซับซ้อนมีพารามิเตอร์จำนวนมากที่สรุปได้ยาก
คุณช่วยกำหนดความสัมพันธ์ระหว่างคุณสมบัติทั้งสี่กับสายพันธุ์ไอริส โดยไม่ต้อง ใช้การเรียนรู้ของเครื่องได้หรือไม่? นั่นคือ คุณสามารถใช้เทคนิคการเขียนโปรแกรมแบบดั้งเดิม (เช่น ประโยคเงื่อนไขจำนวนมาก) เพื่อสร้างแบบจำลองได้หรือไม่ บางที—ถ้าคุณวิเคราะห์ชุดข้อมูลนานพอที่จะระบุความสัมพันธ์ระหว่างการวัดกลีบดอกและกลีบเลี้ยงกับสปีชีส์หนึ่งๆ และสิ่งนี้กลายเป็นเรื่องยาก—อาจเป็นไปไม่ได้—ในชุดข้อมูลที่ซับซ้อนยิ่งขึ้น แนวทางการเรียนรู้ของเครื่องที่ดีเป็น ตัวกำหนดรูปแบบสำหรับคุณ หากคุณป้อนตัวอย่างที่เป็นตัวแทนลงในประเภทโมเดลแมชชีนเลิร์นนิงที่เหมาะสม โปรแกรมจะค้นหาความสัมพันธ์สำหรับคุณ
เลือกรุ่น
เราต้องเลือกชนิดของโมเดลที่จะฝึก มีหลายประเภทและการเลือกรุ่นที่ดีต้องอาศัยประสบการณ์ บทช่วยสอนนี้ใช้โครงข่ายประสาทเทียมเพื่อแก้ปัญหาการจำแนกม่านตา โครงข่ายประสาทเทียม สามารถค้นหาความสัมพันธ์ที่ซับซ้อนระหว่างคุณลักษณะและป้ายกำกับได้ เป็นกราฟที่มีโครงสร้างสูง ซึ่งจัดเป็น เลเยอร์ที่ซ่อนอยู่อย่างน้อยหนึ่งเลเยอร์ แต่ละชั้นที่ซ่อนอยู่ประกอบด้วยเซลล์ประสาทอย่างน้อยหนึ่ง เซลล์ โครงข่ายประสาทมีหลายประเภท และโปรแกรมนี้ใช้โครงข่ายประสาทเทียมแบบหนาแน่นหรือ เชื่อมต่ออย่างสมบูรณ์ : เซลล์ประสาทในชั้นหนึ่งรับการเชื่อมต่ออินพุตจาก ทุก เซลล์ประสาทในชั้นก่อนหน้า ตัวอย่างเช่น รูปที่ 2 แสดงโครงข่ายประสาทเทียมหนาแน่นซึ่งประกอบด้วยชั้นอินพุต เลเยอร์ที่ซ่อนอยู่ 2 ชั้น และเลเยอร์เอาต์พุต:
![]() |
รูปที่ 2 โครงข่ายประสาทเทียมที่มีคุณสมบัติ เลเยอร์ที่ซ่อนอยู่ และการคาดคะเน |
เมื่อแบบจำลองจากรูปที่ 2 ได้รับการฝึกฝนและป้อนตัวอย่างที่ไม่มีป้ายกำกับ จะให้ผลการคาดการณ์สามประการ: โอกาสที่ดอกไม้นี้จะเป็นสายพันธุ์ไอริสที่กำหนด การคาดคะเนนี้เรียกว่าการ อนุมาน สำหรับตัวอย่างนี้ ผลรวมของการทำนายผลลัพธ์คือ 1.0 ในรูปที่ 2 การคาดการณ์นี้แบ่งออกเป็น: 0.02
สำหรับ Iris setosa , 0.95
สำหรับ Iris versicolor และ 0.03
สำหรับ Iris virginica ซึ่งหมายความว่าแบบจำลองคาดการณ์—ด้วยความน่าจะเป็น 95%—ว่าตัวอย่างดอกไม้ที่ไม่มีป้ายกำกับคือ Iris versicolor
สร้างแบบจำลองโดยใช้ Swift for TensorFlow Deep Learning Library
ไลบรารีการเรียนรู้ลึกของ Swift for TensorFlow กำหนดเลเยอร์และแบบแผนดั้งเดิมสำหรับการรวมเข้าด้วยกัน ซึ่งทำให้ง่ายต่อการสร้างแบบจำลองและการทดลอง
โมเดลเป็นโครงสร้างที่สอดคล้องกับ Layer
ซึ่งหมายความว่ามันกำหนดเมธอด struct
callAsFunction(_:)
ที่แมปอินพุต Tensor
กับเอาต์พุต Tensor
callAsFunction(_:)
มักจะจัดลำดับอินพุตผ่านเลเยอร์ย่อย มากำหนด IrisModel
ที่จัดลำดับอินพุตผ่านเลเยอร์ย่อย Dense
สามชั้น
import TensorFlow
let hiddenSize: Int = 10
struct IrisModel: Layer {
var layer1 = Dense<Float>(inputSize: 4, outputSize: hiddenSize, activation: relu)
var layer2 = Dense<Float>(inputSize: hiddenSize, outputSize: hiddenSize, activation: relu)
var layer3 = Dense<Float>(inputSize: hiddenSize, outputSize: 3)
@differentiable
func callAsFunction(_ input: Tensor<Float>) -> Tensor<Float> {
return input.sequenced(through: layer1, layer2, layer3)
}
}
var model = IrisModel()
ฟังก์ชันการเปิดใช้งานจะกำหนดรูปร่างเอาต์พุตของแต่ละโหนดในเลเยอร์ ความไม่เป็นเชิงเส้นเหล่านี้มีความสำคัญ ถ้าไม่มีพวกมัน โมเดลจะเทียบเท่ากับเลเยอร์เดียว มีการเปิดใช้งานมากมาย แต่ ReLU เป็นเรื่องปกติสำหรับเลเยอร์ที่ซ่อนอยู่
จำนวนเลเยอร์และเซลล์ประสาทที่ซ่อนอยู่ในอุดมคตินั้นขึ้นอยู่กับปัญหาและชุดข้อมูล เช่นเดียวกับหลายๆ แง่มุมของแมชชีนเลิร์นนิง การเลือกรูปร่างที่ดีที่สุดของโครงข่ายประสาทเทียมนั้นต้องการการผสมผสานระหว่างความรู้และการทดลอง ตามกฎทั่วไป การเพิ่มจำนวนเลเยอร์ที่ซ่อนอยู่และเซลล์ประสาทมักจะสร้างแบบจำลองที่มีประสิทธิภาพมากขึ้น ซึ่งต้องใช้ข้อมูลมากขึ้นในการฝึกอย่างมีประสิทธิภาพ
การใช้โมเดล
มาดูกันสั้นๆ ว่าโมเดลนี้ทำอะไรกับฟีเจอร์ต่างๆ มากมาย:
// Apply the model to a batch of features.
let firstTrainPredictions = model(firstTrainFeatures)
print(firstTrainPredictions[0..<5])
[[ 1.1514063, -0.7520321, -0.6730235], [ 1.4915676, -0.9158071, -0.9957161], [ 1.0549936, -0.7799266, -0.410466], [ 1.1725322, -0.69009197, -0.8345413], [ 1.4870572, -0.8644099, -1.0958937]]
ที่นี่ แต่ละตัวอย่างจะคืนค่า logit สำหรับแต่ละคลาส
ในการแปลงบันทึกเหล่านี้เป็นความน่าจะเป็นสำหรับแต่ละคลาส ให้ใช้ฟังก์ชัน softmax :
print(softmax(firstTrainPredictions[0..<5]))
[[ 0.7631462, 0.11375094, 0.123102814], [ 0.8523791, 0.076757915, 0.07086295], [ 0.7191151, 0.11478964, 0.16609532], [ 0.77540654, 0.12039323, 0.10420021], [ 0.8541314, 0.08133837, 0.064530246]]ตัวยึดตำแหน่ง23
การนำ argmax
ข้ามคลาสทำให้เราได้ดัชนีคลาสที่คาดการณ์ไว้ แต่ตัวแบบยังไม่ได้รับการฝึกอบรม ดังนั้นจึงไม่ใช่การคาดการณ์ที่ดี
print("Prediction: \(firstTrainPredictions.argmax(squeezingAxis: 1))")
print(" Labels: \(firstTrainLabels)")
Prediction: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] Labels: [1, 1, 0, 1, 2, 2, 0, 2, 1, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 1, 0, 2, 0, 0, 2, 1, 2, 1, 0, 2, 0, 1]
ฝึกโมเดล
การฝึกอบรม เป็นขั้นตอนของการเรียนรู้ของเครื่องเมื่อโมเดลได้รับการปรับให้เหมาะสมทีละน้อย หรือโมเดล เรียนรู้ ชุดข้อมูล เป้าหมายคือการเรียนรู้โครงสร้างของชุดข้อมูลการฝึกอบรมให้เพียงพอเพื่อคาดการณ์ข้อมูลที่มองไม่เห็น หากคุณเรียนรู้ มากเกินไป เกี่ยวกับชุดข้อมูลการฝึก การคาดคะเนจะใช้ได้เฉพาะกับข้อมูลที่เห็นและไม่สามารถสรุปได้ ปัญหานี้เรียกว่า การใส่มากเกินไป — มันเหมือนกับการท่องจำคำตอบแทนที่จะเข้าใจวิธีแก้ปัญหา
ปัญหาการจำแนกม่านตาเป็นตัวอย่างของ การเรียนรู้ของเครื่องภายใต้การดูแล : โมเดลได้รับการฝึกฝนจากตัวอย่างที่มีป้ายกำกับ ใน แมชชีนเลิ ร์นนิงที่ไม่มีผู้ดูแล ตัวอย่างไม่มีป้ายกำกับ โดยปกติแล้ว โมเดลจะค้นหารูปแบบระหว่างคุณลักษณะต่างๆ แทน
เลือกฟังก์ชั่นการสูญเสีย
ทั้งขั้นตอนการฝึกอบรมและการประเมินผลจำเป็นต้องคำนวณการ สูญเสีย ของแบบจำลอง สิ่งนี้วัดว่าการคาดการณ์ของแบบจำลองนั้นมาจากป้ายกำกับที่ต้องการอย่างไร กล่าวอีกนัยหนึ่งว่าแบบจำลองมีประสิทธิภาพการทำงานแย่เพียงใด เราต้องการย่อหรือปรับให้เหมาะสมค่านี้
โมเดลของเราจะคำนวณการสูญเสียโดยใช้ softmaxCrossEntropy(logits:labels:)
ซึ่งใช้การคาดคะเนความน่าจะเป็นของคลาสของโมเดลและป้ายกำกับที่ต้องการ และคืนค่าการสูญเสียเฉลี่ยจากตัวอย่าง
มาคำนวณการสูญเสียสำหรับโมเดลที่ไม่ได้รับการฝึกฝนในปัจจุบัน:
let untrainedLogits = model(firstTrainFeatures)
let untrainedLoss = softmaxCrossEntropy(logits: untrainedLogits, labels: firstTrainLabels)
print("Loss test: \(untrainedLoss)")
Loss test: 1.7598655
สร้างเครื่องมือเพิ่มประสิทธิภาพ
เครื่องมือเพิ่มประสิทธิภาพ ใช้การไล่ระดับสีที่คำนวณกับตัวแปรของแบบจำลองเพื่อลดฟังก์ชัน loss
คุณสามารถนึกถึงฟังก์ชันการสูญเสียเป็นพื้นผิวโค้ง (ดูรูปที่ 3) และเราต้องการหาจุดต่ำสุดโดยเดินไปรอบๆ ความลาดชันชี้ไปในทิศทางของการขึ้นเขาที่ชันที่สุด ดังนั้นเราจะเดินทางตรงกันข้ามและเคลื่อนลงเนิน โดยการคำนวณการสูญเสียและการไล่ระดับสีซ้ำๆ สำหรับแต่ละชุดงาน เราจะปรับแบบจำลองระหว่างการฝึก โมเดลจะค่อยๆ หาส่วนผสมที่ดีที่สุดของตุ้มน้ำหนักและอคติเพื่อลดการสูญเสีย และยิ่งสูญเสียน้อยเท่าไร การคาดการณ์ของแบบจำลองก็จะยิ่งดีขึ้นเท่านั้น
![]() |
รูปที่ 3 อัลกอริธึมการเพิ่มประสิทธิภาพแสดงภาพเมื่อเวลาผ่านไปในพื้นที่ 3 มิติ (ที่มา: Stanford class CS231n , MIT License, เครดิตรูปภาพ: Alec Radford ) |
Swift for TensorFlow มี อัลกอริธึมการ ปรับให้เหมาะสมมากมายสำหรับการฝึกอบรม โมเดลนี้ใช้เครื่องมือเพิ่มประสิทธิภาพ SGD ที่ใช้อัลกอริธึมการ ไล่ระดับสีสุ่ม (SGD) learningRate
กำหนดขนาดขั้นตอนที่จะใช้สำหรับการวนซ้ำแต่ละครั้งจากเนินเขา นี่คือ ไฮเปอร์พารามิเตอร์ ที่คุณมักจะปรับเพื่อให้ได้ผลลัพธ์ที่ดีขึ้น
let optimizer = SGD(for: model, learningRate: 0.01)
ลองใช้ optimizer
เพื่อทำตามขั้นตอนการไล่ระดับสีเดียว อันดับแรก เราคำนวณความลาดชันของการสูญเสียเทียบกับแบบจำลอง:
let (loss, grads) = valueWithGradient(at: model) { model -> Tensor<Float> in
let logits = model(firstTrainFeatures)
return softmaxCrossEntropy(logits: logits, labels: firstTrainLabels)
}
print("Current loss: \(loss)")
Current loss: 1.7598655
ต่อไป เราส่งต่อการไล่ระดับสีที่เราเพิ่งคำนวณไปยังเครื่องมือเพิ่มประสิทธิภาพ ซึ่งจะอัปเดตตัวแปรที่แยกความแตกต่างได้ของโมเดลตามลำดับ:
optimizer.update(&model, along: grads)
หากเราคำนวณการสูญเสียอีกครั้ง มันควรจะน้อยกว่านี้ เพราะการไล่ระดับขั้น (โดยปกติ) จะลดการสูญเสีย:
let logitsAfterOneStep = model(firstTrainFeatures)
let lossAfterOneStep = softmaxCrossEntropy(logits: logitsAfterOneStep, labels: firstTrainLabels)
print("Next loss: \(lossAfterOneStep)")
Next loss: 1.5318773ตัวยึดตำแหน่ง33
วงการฝึก
ด้วยชิ้นส่วนทั้งหมด นางแบบก็พร้อมสำหรับการฝึก! วงจรการฝึกจะดึงตัวอย่างชุดข้อมูลลงในแบบจำลองเพื่อช่วยให้คาดการณ์ได้ดีขึ้น บล็อกรหัสต่อไปนี้ตั้งค่าขั้นตอนการฝึกอบรมเหล่านี้:
- วนซ้ำในแต่ละ ยุค ยุคคือหนึ่งผ่านชุดข้อมูล
- ภายในช่วงเวลา ให้วนซ้ำในแต่ละกลุ่มในยุคการฝึกอบรม
- เปรียบเทียบแบทช์และคว้า คุณสมบัติ (
x
) และ ป้ายกำกับ (y
) - ใช้คุณสมบัติของชุดที่เรียง ทำการคาดคะเนและเปรียบเทียบกับฉลาก วัดความไม่ถูกต้องของการทำนายและใช้เพื่อคำนวณการสูญเสียและการไล่ระดับสีของแบบจำลอง
- ใช้การไล่ระดับสีเพื่ออัปเดตตัวแปรของโมเดล
- ติดตามสถิติบางอย่างสำหรับการแสดงภาพ
- ทำซ้ำสำหรับแต่ละยุค
ตัวแปร epochCount
คือจำนวนครั้งที่วนรอบคอลเล็กชันชุดข้อมูล ตามสัญชาตญาณ การฝึกโมเดลอีกต่อไปไม่ได้รับประกันว่าโมเดลจะดีกว่า epochCount
เป็นไฮเปอร์ พารามิเตอร์ ที่คุณปรับแต่งได้ การเลือกหมายเลขที่เหมาะสมมักต้องใช้ทั้งประสบการณ์และการทดลอง
let epochCount = 500
var trainAccuracyResults: [Float] = []
var trainLossResults: [Float] = []
func accuracy(predictions: Tensor<Int32>, truths: Tensor<Int32>) -> Float {
return Tensor<Float>(predictions .== truths).mean().scalarized()
}
for (epochIndex, epoch) in trainingEpochs.prefix(epochCount).enumerated() {
var epochLoss: Float = 0
var epochAccuracy: Float = 0
var batchCount: Int = 0
for batchSamples in epoch {
let batch = batchSamples.collated
let (loss, grad) = valueWithGradient(at: model) { (model: IrisModel) -> Tensor<Float> in
let logits = model(batch.features)
return softmaxCrossEntropy(logits: logits, labels: batch.labels)
}
optimizer.update(&model, along: grad)
let logits = model(batch.features)
epochAccuracy += accuracy(predictions: logits.argmax(squeezingAxis: 1), truths: batch.labels)
epochLoss += loss.scalarized()
batchCount += 1
}
epochAccuracy /= Float(batchCount)
epochLoss /= Float(batchCount)
trainAccuracyResults.append(epochAccuracy)
trainLossResults.append(epochLoss)
if epochIndex % 50 == 0 {
print("Epoch \(epochIndex): Loss: \(epochLoss), Accuracy: \(epochAccuracy)")
}
}
Epoch 0: Loss: 1.475254, Accuracy: 0.34375 Epoch 50: Loss: 0.91668004, Accuracy: 0.6458333 Epoch 100: Loss: 0.68662673, Accuracy: 0.6979167 Epoch 150: Loss: 0.540665, Accuracy: 0.6979167 Epoch 200: Loss: 0.46283028, Accuracy: 0.6979167 Epoch 250: Loss: 0.4134724, Accuracy: 0.8229167 Epoch 300: Loss: 0.35054502, Accuracy: 0.8958333 Epoch 350: Loss: 0.2731444, Accuracy: 0.9375 Epoch 400: Loss: 0.23622067, Accuracy: 0.96875 Epoch 450: Loss: 0.18956228, Accuracy: 0.96875
เห็นภาพฟังก์ชันการสูญเสียเมื่อเวลาผ่านไป
แม้ว่าการพิมพ์ความคืบหน้าในการฝึกโมเดลจะเป็นประโยชน์ แต่การดูความคืบหน้านี้มักจะเป็นประโยชน์ มากกว่า เราสามารถสร้างแผนภูมิพื้นฐานโดยใช้โมดูล matplotlib
ของ Python
การตีความแผนภูมิเหล่านี้ต้องใช้ประสบการณ์บางอย่าง แต่คุณต้องการเห็นการ สูญเสีย ลดลงและ ความแม่นยำ เพิ่มขึ้น
plt.figure(figsize: [12, 8])
let accuracyAxes = plt.subplot(2, 1, 1)
accuracyAxes.set_ylabel("Accuracy")
accuracyAxes.plot(trainAccuracyResults)
let lossAxes = plt.subplot(2, 1, 2)
lossAxes.set_ylabel("Loss")
lossAxes.set_xlabel("Epoch")
lossAxes.plot(trainLossResults)
plt.show()
Use `print()` to show values.
โปรดทราบว่าแกน y ของกราฟไม่ใช่ศูนย์
ประเมินประสิทธิภาพของตัวแบบ
เมื่อโมเดลได้รับการฝึกอบรมแล้ว เราก็สามารถรับสถิติเกี่ยวกับประสิทธิภาพของโมเดลได้
การ ประเมิน หมายถึงการกำหนดว่าตัวแบบสามารถคาดการณ์ได้อย่างมีประสิทธิภาพเพียงใด ในการพิจารณาประสิทธิภาพของแบบจำลองในการจำแนกประเภทของม่านตา ให้ส่งการวัดกลีบเลี้ยงและกลีบดอกไปยังแบบจำลอง และขอให้แบบจำลองทำนายว่าม่านตาเป็นตัวแทนของชนิดใด จากนั้นเปรียบเทียบการคาดคะเนของแบบจำลองกับฉลากจริง ตัวอย่างเช่น แบบจำลองที่เลือกสปีชีส์ที่ถูกต้องจากตัวอย่างอินพุตครึ่งหนึ่งมี ความแม่นยำ 0.5
รูปที่ 4 แสดงแบบจำลองที่มีประสิทธิภาพมากขึ้นเล็กน้อย โดยได้รับ 4 ใน 5 การคาดการณ์ถูกต้องที่ความแม่นยำ 80%:
ตัวอย่างคุณสมบัติ | ฉลาก | การทำนายแบบจำลอง | |||
---|---|---|---|---|---|
5.9 | 3.0 | 4.3 | 1.5 | 1 | 1 |
6.9 | 3.1 | 5.4 | 2.1 | 2 | 2 |
5.1 | 3.3 | 1.7 | 0.5 | 0 | 0 |
6.0 | 3.4 | 4.5 | 1.6 | 1 | 2 |
5.5 | 2.5 | 4.0 | 1.3 | 1 | 1 |
รูปที่ 4 ลักษณนามม่านตาที่มีความแม่นยำ 80% |
ตั้งค่าชุดข้อมูลทดสอบ
การประเมินแบบจำลองนั้นคล้ายกับการฝึกแบบจำลอง ความแตกต่างที่ใหญ่ที่สุดคือตัวอย่างที่มาจาก ชุดทดสอบ แยกต่างหากแทนที่จะเป็นชุดฝึก เพื่อประเมินประสิทธิภาพของแบบจำลองอย่างเป็นธรรม ตัวอย่างที่ใช้ในการประเมินแบบจำลองต้องแตกต่างจากตัวอย่างที่ใช้ในการฝึกอบรมแบบจำลอง
การตั้งค่าสำหรับชุดข้อมูลการทดสอบคล้ายกับการตั้งค่าสำหรับชุดข้อมูลการฝึก ดาวน์โหลดชุดทดสอบจาก http://download.tensorflow.org/data/iris_test.csv :
let testDataFilename = "iris_test.csv"
download(from: "http://download.tensorflow.org/data/iris_test.csv", to: testDataFilename)
ตอนนี้โหลดลงในอาร์เรย์ของ IrisBatch
es:
let testDataset = loadIrisDatasetFromCSV(
contentsOf: testDataFilename, hasHeader: true,
featureColumns: [0, 1, 2, 3], labelColumns: [4]).inBatches(of: batchSize)
ประเมินแบบจำลองในชุดข้อมูลทดสอบ
ต่างจากขั้นตอนการฝึกอบรม โมเดลจะประเมินข้อมูลการทดสอบเพียงช่วงเดียว เท่านั้น ในเซลล์โค้ดต่อไปนี้ เราทำซ้ำแต่ละตัวอย่างในชุดทดสอบและเปรียบเทียบการคาดการณ์ของโมเดลกับป้ายกำกับจริง ใช้สำหรับวัดความแม่นยำของแบบจำลองทั่วทั้งชุดทดสอบ
// NOTE: Only a single batch will run in the loop since the batchSize we're using is larger than the test set size
for batchSamples in testDataset {
let batch = batchSamples.collated
let logits = model(batch.features)
let predictions = logits.argmax(squeezingAxis: 1)
print("Test batch accuracy: \(accuracy(predictions: predictions, truths: batch.labels))")
}
Test batch accuracy: 0.96666664ตัวยึดตำแหน่ง42
เราจะเห็นได้ในชุดแรก ตัวอย่างเช่น โมเดลมักจะถูกต้อง:
let firstTestBatch = testDataset.first!.collated
let firstTestBatchLogits = model(firstTestBatch.features)
let firstTestBatchPredictions = firstTestBatchLogits.argmax(squeezingAxis: 1)
print(firstTestBatchPredictions)
print(firstTestBatch.labels)
[1, 2, 0, 1, 1, 1, 0, 2, 1, 2, 2, 0, 2, 1, 1, 0, 1, 0, 0, 2, 0, 1, 2, 2, 1, 1, 0, 1, 2, 1] [1, 2, 0, 1, 1, 1, 0, 2, 1, 2, 2, 0, 2, 1, 1, 0, 1, 0, 0, 2, 0, 1, 2, 1, 1, 1, 0, 1, 2, 1]
ใช้แบบจำลองที่ได้รับการฝึกฝนมาในการทำนาย
เราได้ฝึกแบบจำลองและแสดงให้เห็นว่าเป็นการดี—แต่ไม่สมบูรณ์แบบ—ในการจำแนกชนิดของม่านตา ตอนนี้ ลองใช้โมเดลที่ได้รับการฝึกมาเพื่อคาดการณ์ ตัวอย่างที่ไม่มีป้ายกำกับ นั่นคือ ตัวอย่างที่มีคุณลักษณะแต่ไม่มีป้ายกำกับ
ในชีวิตจริง ตัวอย่างที่ไม่มีป้ายกำกับอาจมาจากแหล่งที่มาต่างๆ มากมาย รวมถึงแอป ไฟล์ CSV และฟีดข้อมูล สำหรับตอนนี้ เราจะนำเสนอตัวอย่างที่ไม่มีป้ายกำกับสามตัวอย่างด้วยตนเองเพื่อคาดการณ์ป้ายกำกับ จำได้ว่าหมายเลขฉลากถูกแมปกับการแสดงชื่อเป็น:
-
0
: ไอริส เซโตซ่า -
1
: ไอริส versicolor -
2
: Iris virginica
let unlabeledDataset: Tensor<Float> =
[[5.1, 3.3, 1.7, 0.5],
[5.9, 3.0, 4.2, 1.5],
[6.9, 3.1, 5.4, 2.1]]
let unlabeledDatasetPredictions = model(unlabeledDataset)
for i in 0..<unlabeledDatasetPredictions.shape[0] {
let logits = unlabeledDatasetPredictions[i]
let classIdx = logits.argmax().scalar!
print("Example \(i) prediction: \(classNames[Int(classIdx)]) (\(softmax(logits)))")
}
Example 0 prediction: Iris setosa ([ 0.98731947, 0.012679046, 1.4035809e-06]) Example 1 prediction: Iris versicolor ([0.005065103, 0.85957265, 0.13536224]) Example 2 prediction: Iris virginica ([2.9613977e-05, 0.2637373, 0.73623306])ตัวยึดตำแหน่ง46