דף זה תורגם על ידי Cloud Translation API.
Switch to English

הדרכת הדרכה למודל

צפה ב- TensorFlow.org הפעל בגוגל קולאב צפה במקור ב- GitHub

מדריך זה מציג את Swift עבור TensorFlow על ידי בניית מודל למידת מכונה המסווג פרחי קשתית העין לפי מינים. היא משתמשת בסוויפט עבור TensorFlow כדי:

  1. לבנות מודל,
  2. אימן מודל זה על נתונים לדוגמה, ו-
  3. השתמש במודל לחיזוי נתונים עלומים לא ידועים.

תכנות TensorFlow

מדריך זה משתמש במושגים אלה של Swift ברמה גבוהה עבור TensorFlow:

  • ייבא נתונים באמצעות ה- Epochs API.
  • בנה מודלים באמצעות הפשטות מהירות.
  • השתמש בספריות 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 מינים, אך התוכנית שלנו תסווג רק את השלושה הבאים:

  • איריס סיטוזה
  • איריס וירג'ניקה
  • איריס ורסולור
גיאומטריה של עלי כותרת בהשוואה לשלושה מינים של קשתית העין: קשתית העין, קשתית העין ואיריס ורסיקולור
איור 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 פרחי קשתית העין עם מידות הכיפה ועלי הכותרת. זהו מערך נתונים קלאסי הפופולרי לבעיות סיווג למידת מכונה למתחילים.

ייבא ונתח את מערך ההדרכה

הורד את קובץ הנתונים והמיר אותו למבנה שיכול לשמש את תוכנית סוויפט זו.

הורד את מערך הנתונים

הורד את קובץ מערך האימונים מ- 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). בואו נראה את חמשת הערכים הראשונים.

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 : איריס וירג'ניקה

לקבלת מידע נוסף אודות תכונות ותוויות, עיין בסעיף ML Terminology של קורס קריסת לימוד מכונה .

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

צור מערך נתונים באמצעות ממשק ה- API של Epochs

ה- 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 . בואו נסתכל על היסוד הראשון של העידן הראשון.

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 עבור איריס סטיוזה , 0.95 עבור איריס ורסיקולור , ו 0.03 עבור איריס virginica . משמעות הדבר היא שהמודל צופה - בהסתברות של 95% - שפרח לדוגמא ללא תווית הוא איריס ורסולור .

צור מודל באמצעות ספריית הלמידה העמוקה Swift for TensorFlow

ספריית הלמידה העמוקה Swift for TensorFlow מגדירה שכבות פרימיטיביות ומוסכמות לחיווטם יחדיו, מה שמקל על בניית מודלים והתנסות.

מודל הוא struct התואם את Layer , מה שאומר שהוא מגדיר callAsFunction(_:) הממפה קלט Tensor s לפלט Tensor s. לעתים קרובות שיטת callAsFunction(_:) פשוט רצף את הקלט באמצעות שכבות משנה. בואו נגדיר IrisModel שרצף את הקלט דרך שלוש Dense משנה 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]

תאמן את המודל

אימונים הם השלב של למידת מכונה כאשר המודל מותאם בהדרגה, או שהמודל לומד את מערך הנתונים. המטרה היא ללמוד מספיק על מבנה מערך האימונים כדי לחזות נתונים על סמוי נתונים. אם אתה לומד יותר מדי על מערך האימונים, התחזיות פועלות רק עבור הנתונים שראו ולא יהיו ניתנות להכללה. בעיה זו נקראת התאמת יתר - זה כמו לשנן את התשובות במקום להבין כיצד לפתור בעיה.

בעיית סיווג הקשתית היא דוגמה ללמידת מכונה מפוקחת : המודל מאומן מדוגמאות המכילות תוויות. בלימוד מכונה ללא פיקוח , הדוגמאות אינן מכילות תוויות. במקום זאת, המודל בדרך כלל מוצא דפוסים בין התכונות.

בחר פונקציית אובדן

שני שלבי האימון וההערכה צריכים לחשב את אובדן המודל. זה מודד עד כמה התחזיות של המודל אינן מתווית הרצויה, במילים אחרות, כמה גרוע המודל מבצע. אנו רוצים למזער, או למטב, ערך זה.

המודל שלנו יחשב את ההפסד שלו באמצעות 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 אותו למערך של 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 : איריס 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. אלגוריתמי אופטימיזציה המדמיינים לאורך זמן בחלל תלת ממדי.
(מקור: סטנפורד בכיתה CS231n , רישיון MIT, אשראי תמונה: אלק רדפורד )

ל- Swift for TensorFlow אלגוריתמי אופטימיזציה רבים זמינים לאימון. מודל זה משתמש במייעל ה- SGD המיישם את האלגוריתם של ירידת שיפוע סטוכסטית (SGD). ה- learningRate מגדיר את גודל הצעד שייקח עבור כל איטרציה במורד הגבעה. זהו היפר - פרמטר שתתאים בדרך כלל להשגת תוצאות טובות יותר.