หน้านี้ได้รับการแปลโดย Cloud Translation API
Switch to English

คำแนะนำการฝึกโมเดล

ดูใน TensorFlow.org เรียกใช้ใน Google Colab ดูแหล่งที่มาบน GitHub

คู่มือนี้แนะนำ Swift สำหรับ TensorFlow โดยการสร้างแบบจำลองการเรียนรู้ของเครื่องที่แบ่งประเภทของดอกไอริสตามสายพันธุ์ ใช้ Swift สำหรับ TensorFlow เพื่อ:

  1. สร้างแบบจำลอง
  2. ฝึกโมเดลนี้กับข้อมูลตัวอย่างและ
  3. ใช้แบบจำลองเพื่อคาดการณ์เกี่ยวกับข้อมูลที่ไม่รู้จัก

การเขียนโปรแกรม TensorFlow

คู่มือนี้ใช้แนวคิด Swift สำหรับ TensorFlow ระดับสูงเหล่านี้:

  • นำเข้าข้อมูลด้วย Epochs API
  • สร้างโมเดลโดยใช้ Swift abstractions
  • ใช้ไลบรารี Python โดยใช้การทำงานร่วมกันของ Python ของ Swift เมื่อไม่มีไลบรารี Swift บริสุทธิ์

บทช่วยสอนนี้มีโครงสร้างเหมือนกับโปรแกรม TensorFlow หลายโปรแกรม:

  1. นำเข้าและแยกวิเคราะห์ชุดข้อมูล
  2. เลือกประเภทของโมเดล
  3. ฝึกโมเดล
  4. ประเมินประสิทธิผลของแบบจำลอง
  5. ใช้แบบจำลองที่ได้รับการฝึกฝนเพื่อทำการคาดการณ์

โปรแกรมติดตั้ง

กำหนดค่าการนำเข้า

นำเข้า 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"
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)
}

ปัญหาการจำแนกม่านตา

ลองนึกภาพว่าคุณเป็นนักพฤกษศาสตร์ที่กำลังมองหาวิธีอัตโนมัติในการจัดหมวดหมู่ดอกไอริสแต่ละดอกที่คุณพบ แมชชีนเลิร์นนิงมีอัลกอริทึมมากมายในการจำแนกดอกไม้ในเชิงสถิติ ตัวอย่างเช่นโปรแกรมแมชชีนเลิร์นนิงที่มีความซับซ้อนสามารถจำแนกดอกไม้ตามรูปถ่ายได้ ความทะเยอทะยานของเรามีความเรียบง่ายมากขึ้นเราจะจำแนกดอกไอริสตามขนาดความยาวและความกว้างของ กลีบเลี้ยง และ กลีบดอก

สกุลไอริสมีประมาณ 300 ชนิด แต่โปรแกรมของเราจะจำแนกเฉพาะสามประเภทต่อไปนี้:

  • Iris setosa
  • ไอริสเวอร์จิเนีย
  • ไอริสหลากสี
รูปทรงกลีบดอกเปรียบเทียบกับไอริสสามชนิด ได้แก่ Iris setosa, Iris virginica และ Iris 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())
}
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

จากมุมมองของชุดข้อมูลนี้ให้สังเกตสิ่งต่อไปนี้:

  1. บรรทัดแรกคือส่วนหัวที่มีข้อมูลเกี่ยวกับชุดข้อมูล:
    • มีทั้งหมด 120 ตัวอย่าง แต่ละตัวอย่างมีคุณลักษณะสี่ประการและหนึ่งในสามชื่อป้ายกำกับที่เป็นไปได้
  2. แถวต่อมาคือบันทึกข้อมูลหนึ่ง ตัวอย่าง ต่อบรรทัดโดยที่:
    • สี่ช่องแรกเป็น คุณสมบัติ : นี่คือลักษณะของตัวอย่าง ที่นี่เขตข้อมูลมีตัวเลขลอยแทนการวัดดอกไม้
    • คอลัมน์สุดท้ายคือ ป้ายกำกับ : นี่คือค่าที่เราต้องการทำนาย สำหรับชุดข้อมูลนี้เป็นค่าจำนวนเต็ม 0, 1 หรือ 2 ที่ตรงกับชื่อดอกไม้

ลองเขียนมันออกมาในโค้ด:

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 : ไอริสหลากสี
  • 2 : Iris virginica

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับคุณลักษณะและป้ายกำกับโปรดดู ส่วนคำศัพท์ ML ของหลักสูตร Machine Learning Crash

let classNames = ["Iris setosa", "Iris versicolor", "Iris virginica"]

สร้างชุดข้อมูลโดยใช้ Epochs API

Swift for TensorFlow's Epochs API เป็น 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.0, 2.3, 3.3, 1.0],
 [5.6, 2.9, 3.6, 1.3],
 [5.7, 2.8, 4.1, 1.3],
 [4.4, 2.9, 1.4, 0.2],
 [5.0, 3.2, 1.2, 0.2],
 [4.9, 3.1, 1.5, 0.1],
 [4.9, 3.1, 1.5, 0.1],
 [5.8, 2.6, 4.0, 1.2],
 [5.8, 2.7, 5.1, 1.9],
 [5.0, 3.5, 1.3, 0.3],
 [6.3, 2.5, 5.0, 1.9],
 [4.6, 3.1, 1.5, 0.2],
 [5.9, 3.2, 4.8, 1.8],
 [6.9, 3.2, 5.7, 2.3],
 [5.7, 4.4, 1.5, 0.4],
 [5.8, 2.8, 5.1, 2.4],
 [5.4, 3.0, 4.5, 1.5],
 [6.3, 3.4, 5.6, 2.4],
 [5.9, 3.0, 5.1, 1.8],
 [4.6, 3.4, 1.4, 0.3],
 [5.0, 3.4, 1.6, 0.4],
 [6.3, 2.3, 4.4, 1.3],
 [4.4, 3.0, 1.3, 0.2],
 [5.5, 2.6, 4.4, 1.2],
 [5.4, 3.7, 1.5, 0.2],
 [5.0, 3.4, 1.5, 0.2],
 [6.8, 3.0, 5.5, 2.1],
 [6.2, 2.8, 4.8, 1.8],
 [5.4, 3.9, 1.7, 0.4],
 [7.7, 2.6, 6.9, 2.3],
 [4.9, 3.1, 1.5, 0.1],
 [5.8, 2.7, 4.1, 1.0]]
firstTrainFeatures.shape: [32, 4]
First batch of labels: [1, 1, 1, 0, 0, 0, 0, 1, 2, 0, 2, 0, 1, 2, 0, 2, 1, 2, 2, 0, 0, 1, 0, 1, 0, 0, 2, 2, 0, 2, 0, 1]
firstTrainLabels.shape: [32]

ขอให้สังเกตว่าลักษณะการทำงานสำหรับคนแรก batchSize ตัวอย่างจะถูกจัดกลุ่มเข้าด้วยกัน (หรือแบทช์) ลง 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()

png

None

เลือกประเภทของโมเดล

ทำไมต้องเป็นรุ่น?

โมเดล คือความสัมพันธ์ระหว่างคุณลักษณะและป้ายกำกับ สำหรับปัญหาการจำแนกม่านตาแบบจำลองจะกำหนดความสัมพันธ์ระหว่างการวัดกลีบเลี้ยงและกลีบดอกและชนิดของม่านตาที่คาดการณ์ไว้ โมเดลง่ายๆบางอย่างสามารถอธิบายได้ด้วยพีชคณิตไม่กี่บรรทัด แต่โมเดลแมชชีนเลิร์นนิงที่ซับซ้อนมีพารามิเตอร์จำนวนมากที่สรุปได้ยาก

คุณสามารถระบุความสัมพันธ์ระหว่างคุณสมบัติทั้งสี่และสายพันธุ์ไอริส โดยไม่ ใช้แมชชีนเลิร์นนิงได้หรือไม่ นั่นคือคุณสามารถใช้เทคนิคการเขียนโปรแกรมแบบเดิม (เช่นคำสั่งเงื่อนไขจำนวนมาก) เพื่อสร้างแบบจำลองได้หรือไม่? บางที - ถ้าคุณวิเคราะห์ชุดข้อมูลนานพอที่จะระบุความสัมพันธ์ระหว่างการวัดกลีบดอกและกลีบเลี้ยงกับสิ่งมีชีวิตชนิดใดชนิดหนึ่ง และสิ่งนี้จะกลายเป็นเรื่องยาก - อาจจะเป็นไปไม่ได้บนชุดข้อมูลที่ซับซ้อนขึ้น วิธีการเรียนรู้ของเครื่องที่ดีจะ กำหนดรูปแบบให้คุณ หากคุณป้อนตัวอย่างที่เป็นตัวแทนเพียงพอในประเภทโมเดลแมชชีนเลิร์นนิงที่เหมาะสมโปรแกรมจะค้นหาความสัมพันธ์ให้คุณ

เลือกรุ่น

เราจำเป็นต้องเลือกประเภทของโมเดลที่จะฝึก มีโมเดลหลายประเภทและการเลือกรุ่นที่ดีต้องใช้ประสบการณ์ บทช่วยสอนนี้ใช้โครงข่ายประสาทเพื่อแก้ปัญหาการจำแนกม่านตา โครงข่ายประสาทเทียม สามารถค้นหาความสัมพันธ์ที่ซับซ้อนระหว่างคุณลักษณะและป้ายกำกับ เป็นกราฟที่มีโครงสร้างสูงจัดเป็น เลเยอร์ที่ซ่อนอยู่ อย่างน้อยหนึ่ง เลเยอร์ แต่ละชั้นที่ซ่อนอยู่ประกอบด้วย เซลล์ประสาท อย่างน้อยหนึ่ง เซลล์ เครือข่ายประสาทเทียมมีหลายประเภทและโปรแกรมนี้ใช้เครือข่ายประสาทที่หนาแน่นหรือ เชื่อมต่อกันอย่างเต็มที่ : เซลล์ประสาทในชั้นเดียวจะรับการเชื่อมต่ออินพุตจากเซลล์ประสาท ทุก เซลล์ในชั้นก่อนหน้า ตัวอย่างเช่นรูปที่ 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 Deep Learning Library กำหนดเลเยอร์และรูปแบบดั้งเดิมสำหรับการต่อสายเข้าด้วยกันซึ่งทำให้ง่ายต่อการสร้างโมเดลและการทดลอง

รูปแบบเป็น struct ที่สอดคล้องกับ Layer ซึ่งหมายความว่ามันกำหนด callAsFunction(_:) วิธีการป้อนข้อมูลที่แม Tensor เพื่อการส่งออก Tensor s วิธี 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)
firstTrainPredictions[0..<5]
[[ -0.8188498,   1.5179185,  -0.3823631],
 [ -0.8939362,   1.8654141,   -0.431251],
 [  -0.877989,   1.6670536, -0.37928653],
 [-0.62468827,   1.7252572,  -0.3766331],
 [-0.68565977,   2.0301576, -0.44697276]]

ที่นี่แต่ละตัวอย่างจะส่งคืน logit สำหรับแต่ละคลาส

ในการแปลงไฟล์บันทึกเหล่านี้เป็นความน่าจะเป็นสำหรับแต่ละคลาสให้ใช้ฟังก์ชัน softmax :

softmax(firstTrainPredictions[0..<5])
[[ 0.07754943,   0.8024614, 0.119989246],
 [ 0.05441314,   0.8591607,   0.0864262],
 [ 0.06497577,   0.8280362,  0.10698802],
 [ 0.07832983,   0.8212881, 0.100382075],
 [0.057515744,   0.8694633,   0.0730209]]

การใช้ argmax ข้ามคลาสทำให้เราได้ดัชนีคลาสที่คาดการณ์ไว้ แต่โมเดลยังไม่ได้รับการฝึกฝนดังนั้นนี่จึงไม่ใช่การคาดเดาที่ดี

print("Prediction: \(firstTrainPredictions.argmax(squeezingAxis: 1))")
print("    Labels: \(firstTrainLabels)")
Prediction: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
    Labels: [1, 1, 1, 0, 0, 0, 0, 1, 2, 0, 2, 0, 1, 2, 0, 2, 1, 2, 2, 0, 0, 1, 0, 1, 0, 0, 2, 2, 0, 2, 0, 1]

ฝึกโมเดล

การฝึกอบรม เป็นขั้นตอนของการเรียนรู้ของเครื่องเมื่อโมเดลค่อยๆปรับให้เหมาะสมหรือโมเดล เรียนรู้ ชุดข้อมูล เป้าหมายคือการเรียนรู้เกี่ยวกับโครงสร้างของชุดข้อมูลการฝึกอบรมให้เพียงพอเพื่อคาดการณ์เกี่ยวกับข้อมูลที่มองไม่เห็น หากคุณเรียนรู้ มากเกินไป เกี่ยวกับชุดข้อมูลการฝึกอบรมการคาดการณ์จะใช้ได้เฉพาะกับข้อมูลที่ได้เห็นเท่านั้นและจะไม่สามารถสรุปได้ทั่วไป ปัญหานี้เรียกว่า overfitting - เหมือนกับการจำคำตอบแทนที่จะเข้าใจวิธีแก้ปัญหา

ปัญหาการจำแนกม่านตาเป็นตัวอย่างของ การเรียนรู้ ของ เครื่องภายใต้การดูแล : แบบจำลองได้รับการฝึกฝนจากตัวอย่างที่มีป้ายกำกับ ใน แมชชีนเลิร์นนิง ที่ไม่ได้รับการดูแลตัวอย่างจะไม่มีป้ายกำกับ แต่โดยทั่วไปแล้วโมเดลจะพบรูปแบบระหว่างคุณสมบัติต่างๆ

เลือกฟังก์ชันการสูญเสีย

ทั้งขั้นตอนการฝึกอบรมและการประเมินผลจำเป็นต้องคำนวณการ สูญเสีย ของแบบจำลอง สิ่งนี้จะวัดการคาดคะเนของโมเดลจากป้ายกำกับที่ต้องการกล่าวอีกนัยหนึ่งคือโมเดลมีประสิทธิภาพแย่เพียงใด เราต้องการย่อหรือเพิ่มประสิทธิภาพค่านี้

แบบจำลองของเราจะคำนวณการสูญเสียโดยใช้ softmaxCrossEntropy(logits:labels:) ซึ่งใช้การคาดคะเนความน่าจะเป็นของคลาสของโมเดลและป้ายกำกับที่ต้องการและส่งกลับค่าเฉลี่ยการสูญเสียในตัวอย่าง

ลองคำนวณการสูญเสียสำหรับโมเดลที่ไม่ได้รับการฝึกฝนในปัจจุบัน:

let untrainedLogits = model(firstTrainFeatures)
let untrainedLoss = softmaxCrossEntropy(logits: untrainedLogits, labels: firstTrainLabels)
print("Loss test: \(untrainedLoss)")
Loss test: 1.8843782

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.8843782

ต่อไปเราจะส่งการไล่ระดับสีที่เราเพิ่งคำนวณไปยังเครื่องมือเพิ่มประสิทธิภาพซึ่งจะอัปเดตตัวแปรที่แตกต่างกันของโมเดลตาม:

optimizer.update(&model, along: grads)

หากเราคำนวณการสูญเสียอีกครั้งควรมีขนาดเล็กลงเนื่องจากขั้นตอนการไล่ระดับสี (โดยปกติ) จะลดการสูญเสีย:

let logitsAfterOneStep = model(firstTrainFeatures)
let lossAfterOneStep = softmaxCrossEntropy(logits: logitsAfterOneStep, labels: firstTrainLabels)
print("Next loss: \(lossAfterOneStep)")
Next loss: 1.327492

ห่วงการฝึก

โมเดลก็พร้อมสำหรับการฝึกซ้อม! ลูปการฝึกจะดึงตัวอย่างชุดข้อมูลลงในแบบจำลองเพื่อช่วยให้คาดการณ์ได้ดีขึ้น บล็อกโค้ดต่อไปนี้ตั้งค่าขั้นตอนการฝึกอบรมเหล่านี้:

  1. ทำซ้ำในแต่ละ ยุค ยุคหนึ่งคือการส่งผ่านชุดข้อมูล
  2. ภายในช่วงเวลาหนึ่งให้ทำซ้ำในแต่ละชุดในยุคการฝึกอบรม
  3. เรียงชุดแบทช์และจับ คุณสมบัติ ( x ) และ ป้ายกำกับ ( y )
  4. ใช้คุณลักษณะของชุดงานที่เรียงต่อกันทำการคาดคะเนและเปรียบเทียบกับป้ายกำกับ วัดความไม่ถูกต้องของการคาดการณ์และใช้เพื่อคำนวณการสูญเสียและการไล่ระดับของโมเดล
  5. ใช้การไล่ระดับสีเพื่ออัปเดตตัวแปรของโมเดล
  6. ติดตามสถิติบางอย่างสำหรับการแสดงภาพ
  7. ทำซ้ำสำหรับแต่ละยุค

ตัวแปร epochCount คือจำนวนครั้งในการวนซ้ำในคอลเล็กชันชุดข้อมูล ในทางตรงกันข้ามการฝึกนางแบบอีกต่อไปไม่ได้รับประกันว่าจะได้โมเดลที่ดีกว่า epochCount เป็น hyperparameter ที่คุณสามารถปรับแต่ง การเลือกหมายเลขที่เหมาะสมมักต้องใช้ทั้งประสบการณ์และการทดลอง

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.2097169, Accuracy: 0.4375
Epoch 50: Loss: 0.52431935, Accuracy: 0.875
Epoch 100: Loss: 0.3710712, Accuracy: 0.8958333
Epoch 150: Loss: 0.2160871, Accuracy: 0.9583333
Epoch 200: Loss: 0.14524944, Accuracy: 0.9791667
Epoch 250: Loss: 0.12487585, Accuracy: 0.9583333
Epoch 300: Loss: 0.122261345, Accuracy: 0.9791667
Epoch 350: Loss: 0.10571604, Accuracy: 0.9895833
Epoch 400: Loss: 0.09489065, Accuracy: 0.9791667
Epoch 450: Loss: 0.09057075, Accuracy: 0.9895833

เห็นภาพฟังก์ชันการสูญเสียเมื่อเวลาผ่านไป

แม้ว่าการพิมพ์ความคืบหน้าการฝึกอบรมของโมเดลจะเป็นประโยชน์ แต่การดูความคืบหน้านี้มักจะเป็นประโยชน์ มากกว่า เราสามารถสร้างแผนภูมิพื้นฐานโดยใช้โมดูล 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()

png

None

โปรดทราบว่าแกน 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

เราสามารถเห็นได้ในชุดแรกเช่นโมเดลมักจะถูกต้อง:

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 : ไอริสหลากสี
  • 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.9872727,  0.0124736205, 0.00025379486])
Example 1 prediction: Iris versicolor ([0.0026452888,     0.973102,  0.024252668])
Example 2 prediction: Iris virginica ([9.066205e-05,    0.0736953,    0.9262141])

สร้างเครื่องมือเพิ่มประสิทธิภาพ

เครื่องมือเพิ่มประสิทธิภาพ จะใช้การไล่ระดับสีที่คำนวณกับตัวแปรของโมเดลเพื่อลดฟังก์ชัน loss ให้น้อยที่สุด คุณสามารถนึกถึงฟังก์ชันการสูญเสียเป็นพื้นผิวโค้ง (ดูรูปที่ 3) และเราต้องการหาจุดต่ำสุดโดยการเดินไปรอบ ๆ การไล่ระดับจะชี้ไปในทิศทางของการขึ้นที่สูงชันที่สุด - ดังนั้นเราจะเดินทางไปในทางตรงกันข้ามและเคลื่อนตัวลงเขา โดยการคำนวณการสูญเสียและการไล่ระดับสีซ้ำ ๆ สำหรับแต่ละชุดเราจะปรับโมเดลระหว่างการฝึก แบบจำลองจะค่อยๆหาน้ำหนักและอคติร่วมกันเพื่อลดการสูญเสียให้น้อยที่สุด และยิ่งขาดทุนน้อยเท่าไหร่การคาดการณ์ของโมเดลก็จะยิ่งดีขึ้นเท่านั้น

อัลกอริธึมการเพิ่มประสิทธิภาพแสดงภาพเมื่อเวลาผ่านไปในพื้นที่ 3 มิติ
รูปที่ 3. อัลกอริธึมการเพิ่มประสิทธิภาพแสดงภาพเมื่อเวลาผ่านไปในพื้นที่ 3 มิติ
(ที่มา: Stanford class CS231n , MIT License, Image credit: Alec Radford )

Swift สำหรับ TensorFlow มี อัลกอริธึมการเพิ่มประสิทธิภาพ มากมายสำหรับการฝึกอบรม รุ่นนี้ใช้เครื่องมือเพิ่มประสิทธิภาพ SGD ที่ใช้อัลกอริทึมการ ไล่ระดับสีสุ่ม (SGD) อัตรา learningRate กำหนดขนาดขั้นตอนที่จะใช้สำหรับการวนซ้ำแต่ละครั้งลงจากเนินเขา นี่คือ ไฮเปอร์พารามิเตอร์ ที่คุณมักจะปรับเปลี่ยนเพื่อให้ได้ผลลัพธ์ที่ดีขึ้น