3.6. Sıfırdan Softmaks Regresyon Uygulaması Yaratma
Open the notebook in Colab
Open the notebook in Colab
Open the notebook in Colab
Open the notebook in SageMaker Studio Lab

Sıfırdan lineer regresyon uyguladığımız gibi, softmax regresyonunun da benzer şekilde temel olduğuna ve kanlı ayrıntılarını ve nasıl kendinizin uygulanacağını bilmeniz gerektiğine inanıyoruz. Section 3.5 içinde yeni eklenen Fashion-MNIST veri kümesiyle çalışacağız, grup boyutu 256 olan bir veri yineleyicisi kuracağız.

from IPython import display
from d2l import mxnet as d2l
from mxnet import autograd, gluon, np, npx

npx.set_np()

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
import torch
from IPython import display
from d2l import torch as d2l

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
from IPython import display
import tensorflow as tf
from d2l import tensorflow as d2l

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

3.6.1. Model Parametrelerini İlkletme

Doğrusal regresyon örneğimizde olduğu gibi, buradaki her örnek sabit uzunlukta bir vektörle temsil edilecektir. Ham veri kümesindeki her örnek \(28 \times 28\)’lik görseldir. Bu bölümde, her bir görseli 784 uzunluğundaki vektörler olarak ele alarak düzleştireceğiz. Gelecekte, görsellerdeki uzamsal yapıyı kullanmak için daha karmaşık stratejilerden bahsedeceğiz, ancak şimdilik her piksel konumunu yalnızca başka bir öznitelik olarak ele alıyoruz.

Softmaks regresyonunda, sınıflar kadar çıktıya sahip olduğumuzu hatırlayın. Veri kümemiz 10 sınıf içerdiğinden, ağımızın çıktı boyutu 10 olacaktır. Sonuç olarak, ağırlıklarımız \(784 \times 10\) matrisi ve ek girdiler \(1 \times 10\) satır vektörü oluşturacaktır. Doğrusal regresyonda olduğu gibi, W ağırlıklarımızı Gauss gürültüsüyle ve ek girdilerimizi ilk değerini 0 alacak şekilde başlatacağız.

num_inputs = 784
num_outputs = 10

W = np.random.normal(0, 0.01, (num_inputs, num_outputs))
b = np.zeros(num_outputs)
W.attach_grad()
b.attach_grad()
num_inputs = 784
num_outputs = 10

W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
b = torch.zeros(num_outputs, requires_grad=True)
num_inputs = 784
num_outputs = 10

W = tf.Variable(tf.random.normal(shape=(num_inputs, num_outputs),
                                 mean=0, stddev=0.01))
b = tf.Variable(tf.zeros(num_outputs))

3.6.2. Softmaks İşlemini Tanımlama

Softmaks regresyon modelini uygulamadan önce, toplam operatörünün bir tensörde belirli boyutlar boyunca nasıl çalıştığını Section 2.3.6 ve Section 2.3.6.1 içinde anlatıldığı gibi kısaca gözden geçirelim. Bir X matrisi verildiğinde, tüm öğeleri (varsayılan olarak) veya yalnızca aynı eksendeki öğeleri toplayabiliriz, örneğin aynı sütun (eksen 0) veya aynı satır (eksen 1) üzerinden. X (2, 3) şeklinde bir tensör ise ve sütunları toplarsak, sonucun (3,) şeklinde bir vektör olacağını unutmayın. Toplam operatörünü çağırırken, üzerinde topladığımız boyutu daraltmak yerine esas tensördeki eksen sayısını korumayı belirtebiliriz. Bu, (1, 3) şeklinde iki boyutlu bir tensörle sonuçlanacaktır.

X = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
X.sum(0, keepdims=True), X.sum(1, keepdims=True)
(array([[5., 7., 9.]]),
 array([[ 6.],
        [15.]]))
X = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
X.sum(0, keepdim=True), X.sum(1, keepdim=True)
(tensor([[5., 7., 9.]]),
 tensor([[ 6.],
         [15.]]))
X = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
tf.reduce_sum(X, 0, keepdims=True), tf.reduce_sum(X, 1, keepdims=True)
(<tf.Tensor: shape=(1, 3), dtype=float32, numpy=array([[5., 7., 9.]], dtype=float32)>,
 <tf.Tensor: shape=(2, 1), dtype=float32, numpy=
 array([[ 6.],
        [15.]], dtype=float32)>)

Artık softmaks işlemini uygulamaya hazırız. Softmaks’ın üç adımdan oluştuğunu hatırlayın: (i) Her terimin üssünü alıyoruz (exp kullanarak); (ii) her bir örnek için normalleştirme sabitini elde etmek için her satırı topluyoruz (toplu işte (batch) örnek başına bir satırımız vardır); (iii) her satırı normalleştirme sabitine bölerek sonucun toplamının 1 olmasını sağlıyoruz. Koda bakmadan önce, bunun bir denklem olarak nasıl ifade edildiğini hatırlayalım:

(3.6.1)\[\mathrm{softmax}(\mathbf{X})_{ij} = \frac{\exp(\mathbf{X}_{ij})}{\sum_k \exp(\mathbf{X}_{ik})}.\]

Payda veya normalleştirme sabiti bazen bölmeleme fonksiyonu olarak da adlandırılır (ve logaritmasına log-bölmeleme fonksiyonu denir). Bu ismin kökenleri, ilgili bir denklemin bir parçacıklar topluluğu üzerindeki dağılımı modellediği istatistiksel fizik’tedir.

def softmax(X):
    X_exp = np.exp(X)
    partition = X_exp.sum(1, keepdims=True)
    return X_exp / partition  # Burada yayin mekanizmasi uygulanir
def softmax(X):
    X_exp = torch.exp(X)
    partition = X_exp.sum(1, keepdim=True)
    return X_exp / partition  # Burada yayin mekanizmasi uygulanir
def softmax(X):
    X_exp = tf.exp(X)
    partition = tf.reduce_sum(X_exp, 1, keepdims=True)
    return X_exp / partition  # Burada yayin mekanizmasi uygulanir

Gördüğünüz gibi, herhangi bir rastgele girdi için, her bir öğeyi negatif olmayan bir sayıya dönüştürüyoruz. Ayrıca, olasılık belirtmek için gerektiği gibi, her satırın toplamı 1’dir.

X = np.random.normal(0, 1, (2, 5))
X_prob = softmax(X)
X_prob, X_prob.sum(1)
(array([[0.22376052, 0.06659239, 0.06583703, 0.29964197, 0.3441681 ],
        [0.63209665, 0.03179282, 0.194987  , 0.09209415, 0.04902935]]),
 array([1.        , 0.99999994]))
X = torch.normal(0, 1, (2, 5))
X_prob = softmax(X)
X_prob, X_prob.sum(1)
(tensor([[0.0155, 0.2640, 0.5381, 0.1014, 0.0810],
         [0.7024, 0.0739, 0.0257, 0.0696, 0.1284]]),
 tensor([1.0000, 1.0000]))
X = tf.random.normal((2, 5), 0, 1)
X_prob = softmax(X)
X_prob, tf.reduce_sum(X_prob, 1)
(<tf.Tensor: shape=(2, 5), dtype=float32, numpy=
 array([[0.01950961, 0.11577977, 0.3813738 , 0.12239581, 0.36094102],
        [0.08264619, 0.3784814 , 0.38132882, 0.05693069, 0.10061286]],
       dtype=float32)>,
 <tf.Tensor: shape=(2,), dtype=float32, numpy=array([1., 1.], dtype=float32)>)

Bu matematiksel olarak doğru görünse de, uygulamamızda biraz özensiz davrandık çünkü matrisin büyük veya çok küçük öğeleri nedeniyle sayısal taşma (overflow) veya küçümenliğe (underflow) karşı önlem alamadık.

3.6.3. Modeli Tanımlama

Artık softmaks işlemini tanımladığımıza göre, softmaks regresyon modelini uygulayabiliriz. Aşağıdaki kod, girdinin ağ üzerinden çıktıya nasıl eşlendiğini tanımlar. Verileri modelimizden geçirmeden önce, reshape işlevini kullanarak toplu işteki (batch) her esas görseli bir vektör halinde düzleştirdiğimize dikkat edin.

def net(X):
    return softmax(np.dot(X.reshape((-1, W.shape[0])), W) + b)
def net(X):
    return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)
def net(X):
    return softmax(tf.matmul(tf.reshape(X, (-1, W.shape[0])), W) + b)

3.6.4. Kayıp Fonksiyonunu Tanımlama

Daha sonra, Section 3.4 içinde tanıtıldığı gibi, çapraz entropi kaybı işlevini uygulamamız gerekir. Bu, tüm derin öğrenmede en yaygın kayıp işlevi olabilir, çünkü şu anda sınıflandırma sorunları regresyon sorunlarından çok daha fazladır.

Çapraz entropinin, gerçek etikete atanan tahmin edilen olasılığın negatif log-olabilirliğini aldığını hatırlayın. Bir Python for-döngüsü (verimsiz olma eğilimindedir) ile tahminler üzerinde yinelemek yerine, tüm öğeleri tek bir operatörle seçebiliriz. Aşağıda, 3 sınıf üzerinde tahmin edilen olasılıkların 2 örneğini ve bunlara karşılık gelen y etiketleriyle y_hat örnek verilerini oluşturuyoruz. y ile, ilk örnekte birinci sınıfın doğru tahmin olduğunu biliyoruz ve ikinci örnekte üçüncü sınıf temel referans değerdir. y’yi y_hat içindeki olasılıkların indeksleri olarak kullanarak, ilk örnekte birinci sınıfın olasılığını ve ikinci örnekte üçüncü sınıfın olasılığını seçiyoruz.

Aşağıda, 3 sınıf üzerinden tahmin edilen olasılıkların 2 örneğini içeren bir oyuncak verisi y_hat’i oluşturuyoruz. Ardından birinci örnekte birinci sınıfın olasılığını ve ikinci örnekte üçüncü sınıfın olasılığını seçiyoruz.

y = np.array([0, 2])
y_hat = np.array([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
y_hat[[0, 1], y]
array([0.1, 0.5])
y = torch.tensor([0, 2])
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
y_hat[[0, 1], y]
tensor([0.1000, 0.5000])
y_hat = tf.constant([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
y = tf.constant([0, 2])
tf.boolean_mask(y_hat, tf.one_hot(y, depth=y_hat.shape[-1]))
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([0.1, 0.5], dtype=float32)>

Artık, çapraz entropi kaybı işlevini tek bir kod satırı ile verimli bir şekilde uygulayabiliriz.

def cross_entropy(y_hat, y):
    return - np.log(y_hat[range(len(y_hat)), y])

cross_entropy(y_hat, y)
array([2.3025851, 0.6931472])
def cross_entropy(y_hat, y):
    return - torch.log(y_hat[range(len(y_hat)), y])

cross_entropy(y_hat, y)
tensor([2.3026, 0.6931])
def cross_entropy(y_hat, y):
    return -tf.math.log(tf.boolean_mask(
        y_hat, tf.one_hot(y, depth=y_hat.shape[-1])))

cross_entropy(y_hat, y)
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([2.3025851, 0.6931472], dtype=float32)>

3.6.5. Sınıflandırma Doğruluğu

Tahmin edilen olasılık dağılımı y_hat göz önüne alındığında, genellikle kesin bir tahmin vermemiz gerektiğinde tahmin edilen en yüksek olasılığa sahip sınıfı seçeriz. Aslında, birçok uygulama bir seçim yapmamızı gerektirir. Gmail, bir e-postayı “Birincil”, “Sosyal”, “Güncellemeler” veya “Forumlar” olarak sınıflandırmalıdır. Olasılıkları dahili olarak tahmin edebilir, ancak günün sonunda sınıflar arasından birini seçmesi gerekir.

Tahminler y etiket sınıfıyla tutarlı olduğunda doğrudur. Sınıflandırma doğruluğu, doğru olan tüm tahminlerin oranıdır. Doğruluğu doğrudan optimize etmek zor olabilse de (türevleri alınamaz), genellikle en çok önemsediğimiz performans ölçütüdür ve sınıflandırıcıları eğitirken neredeyse her zaman onu rapor edeceğiz.

Doğruluğu hesaplamak için aşağıdakileri yapıyoruz. İlk olarak, y_hat bir matris ise, ikinci boyutun her sınıf için tahmin puanlarını sakladığını varsayıyoruz. Her satırdaki en büyük girdi için dizine göre tahmin edilen sınıfı elde ederken argmax kullanırız. Ardından, tahmin edilen sınıfı gerçek referans değer y ile karşılaştırırız. Eşitlik operatörü == veri türlerine duyarlı olduğundan, y_hat veri türünü y ile eşleşecek şekilde dönüştürürüz. Sonuç, 0 (yanlış) ve 1 (doğru) girişlerini içeren bir tensördür. Toplamlarını almak doğru tahminlerin sayısını verir.

def accuracy(y_hat, y):  #@save
    """Doğru tahminlerin sayısını hesaplayın."""
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        y_hat = y_hat.argmax(axis=1)
    cmp = y_hat.astype(y.dtype) == y
    return float(cmp.astype(y.dtype).sum())
def accuracy(y_hat, y):  #@save
    """Doğru tahminlerin sayısını hesaplayın."""
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        y_hat = y_hat.argmax(axis=1)
    cmp = y_hat.type(y.dtype) == y
    return float(cmp.type(y.dtype).sum())
def accuracy(y_hat, y):  #@save
    """Doğru tahminlerin sayısını hesaplayın."""
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        y_hat = tf.argmax(y_hat, axis=1)
    cmp = tf.cast(y_hat, y.dtype) == y
    return float(tf.reduce_sum(tf.cast(cmp, y.dtype)))

Önceden tanımlanan y_hat ve y değişkenlerini sırasıyla tahmin edilen olasılık dağılımları ve etiketler olarak kullanmaya devam edeceğiz. İlk örneğin tahmin sınıfının 2 olduğunu görebiliriz (satırın en büyük öğesi dizin 2 ile 0.6’dır), bu gerçek etiket, 0 ile tutarsızdır. İkinci örneğin tahmin sınıfı 2’dir (satırın en büyük öğesi 2 endeksi ile 0.5’tir) ve bu gerçek etiket 2 ile tutarlıdır. Bu nedenle, bu iki örnek için sınıflandırma doğruluk oranı 0.5’tir.

accuracy(y_hat, y) / len(y)
0.5
accuracy(y_hat, y) / len(y)
0.5
accuracy(y_hat, y) / len(y)
0.5

Benzer şekilde, veri yineleyici data_iter aracılığıyla erişilen bir veri kümesindeki herhangi bir net modelinin doğruluğunu hesaplayabiliriz.

def evaluate_accuracy(net, data_iter):  #@save
    """Bir veri kümesinde bir modelin doğruluğunu hesaplayın."""
    metric = Accumulator(2)  # Doğru tahmin sayısı, tahmin sayısı
    for X, y in data_iter:
        metric.add(accuracy(net(X), y), d2l.size(y))
    return metric[0] / metric[1]
def evaluate_accuracy(net, data_iter):  #@save
    """Bir veri kumesinde bir modelin doğruluğunu hesaplayın."""
    if isinstance(net, torch.nn.Module):
        net.eval()  # Modeli değerlendirme moduna kurun
    metric = Accumulator(2)  # Doğru tahmin sayısı, tahmin sayısı

    with torch.no_grad():
        for X, y in data_iter:
            metric.add(accuracy(net(X), y), y.numel())
    return metric[0] / metric[1]
def evaluate_accuracy(net, data_iter):  #@save
    """Bir veri kümesinde bir modelin doğruluğunu hesaplayın."""
    metric = Accumulator(2)  # Doğru tahmin sayısı, tahmin sayısı
    for X, y in data_iter:
        metric.add(accuracy(net(X), y), d2l.size(y))
    return metric[0] / metric[1]

Burada, Accumulator, birden çok değişken üzerindeki toplamları biriktirmek için bir yardımcı sınıftır. Yukarıdaki evaluate_accuracy işlevinde, Accumulator örneğinde sırasıyla hem doğru tahminlerin sayısını hem de tahminlerin sayısını depolamak için 2 değişken oluştururuz. Veri kümesini yineledikçe her ikisi de zaman içinde birikecektir.

class Accumulator:  #@save
    """`n` değişken üzerinden toplamları biriktirmek için"""
    def __init__(self, n):
        self.data = [0.0] * n

    def add(self, *args):
        self.data = [a + float(b) for a, b in zip(self.data, args)]

    def reset(self):
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        return self.data[idx]

net modelini rastgele ağırlıklarla başlattığımız için, bu modelin doğruluğu rastgele tahmin etmeye yakın olmalıdır, yani 10 sınıf için 0.1 gibi.

evaluate_accuracy(net, test_iter)
0.0811
evaluate_accuracy(net, test_iter)
0.0979
evaluate_accuracy(net, test_iter)
0.0797

3.6.6. Eğitim

Softmaks regresyonu için eğitim döngüsü, Section 3.2 içindeki doğrusal regresyon uygulamamızı okursanız, çarpıcı bir şekilde tanıdık gelebilir. Burada uygulamayı yeniden kullanılabilir hale getirmek için yeniden düzenliyoruz. İlk olarak, bir dönemi (epoch) eğitmek için bir işlev tanımlıyoruz. updater’in, grup boyutunu bağımsız değişken olarak kabul eden, model parametrelerini güncellemek için genel bir işlev olduğuna dikkat edin. d2l.sgd işlevinin bir sarmalayıcısı (wrapper) veya bir çerçevenin yerleşik optimizasyon işlevi olabilir.

def train_epoch_ch3(net, train_iter, loss, updater):  #@save
    """Bir modeli bir dönem içinde eğitme (Bölüm 3'te tanımlanmıştır)."""
    # Eğitim kaybı toplamı, eğitim doğruluğu toplamı, örnek sayısı
    metric = Accumulator(3)
    if isinstance(updater, gluon.Trainer):
        updater = updater.step
    for X, y in train_iter:
        # Gradyanları hesaplayın ve parametreleri güncelleyin
        with autograd.record():
            y_hat = net(X)
            l = loss(y_hat, y)
        l.backward()
        updater(X.shape[0])
        metric.add(float(l.sum()), accuracy(y_hat, y), y.size)
    # Eğitim kaybını ve doğruluğunu döndür
    return metric[0] / metric[2], metric[1] / metric[2]
def train_epoch_ch3(net, train_iter, loss, updater):  #@save
    """Bölüm 3'te tanımlanan eğitim döngüsü."""
    # Modeli eğitim moduna kurun
    if isinstance(net, torch.nn.Module):
        net.train()
    # Eğitim kaybı toplamı, eğitim doğruluğu toplamı, örnek sayısı
    metric = Accumulator(3)
    for X, y in train_iter:
        # Gradyanları hesaplayın ve parametreleri güncelleyin
        y_hat = net(X)
        l = loss(y_hat, y)
        if isinstance(updater, torch.optim.Optimizer):
            # PyTorch yerleşik optimize edicisini ve kayıp kriterini kullanma
            updater.zero_grad()
            l.mean().backward()
            updater.step()
        else:
            # Özel olarak oluşturulmuş optimize edicisini ve kayıp ölçütünü kullanma
            l.sum().backward()
            updater(X.shape[0])
        metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    # Eğitim kaybını ve doğruluğunu döndür
    return metric[0] / metric[2], metric[1] / metric[2]
def train_epoch_ch3(net, train_iter, loss, updater):  #@save
    """Bölüm 3'te tanımlanan eğitim döngüsü."""
     # Eğitim kaybı toplamı, eğitim doğruluğu toplamı, örnek sayısı
    metric = Accumulator(3)
    for X, y in train_iter:
        # Gradyanları hesaplayın ve parametreleri güncelleyin
        with tf.GradientTape() as tape:
            y_hat = net(X)
            # Kullanıcıların bu kitapta uygulayabilecekleri (tahminler, etiketler)
            # yerine kayıp alımları (etiketler, tahminler) için Keras uygulamaları
            # ör. yukarıda uyguladığımız `cross_entropy`
            if isinstance(loss, tf.keras.losses.Loss):
                l = loss(y, y_hat)
            else:
                l = loss(y_hat, y)
        if isinstance(updater, tf.keras.optimizers.Optimizer):
            params = net.trainable_variables
            grads = tape.gradient(l, params)
            updater.apply_gradients(zip(grads, params))
        else:
            updater(X.shape[0], tape.gradient(l, updater.params))
        # Varsayılan olarak Keras kaybı, bir toplu iş içindeki ortalama kaybı döndürür
        l_sum = l * float(tf.size(y)) if isinstance(
            loss, tf.keras.losses.Loss) else tf.reduce_sum(l)
        metric.add(l_sum, accuracy(y_hat, y), tf.size(y))
     # Eğitim kaybını ve doğruluğunu döndür
    return metric[0] / metric[2], metric[1] / metric[2]

Eğitim işlevinin uygulamasını göstermeden önce, verileri animasyonda (canlandırma) çizen bir yardımcı program sınıfı tanımlıyoruz. Yine kitabın geri kalanında kodu basitleştirmeyi amaçlamaktadır.

class Animator:  #@save
    """Animasyonda veri çizdirme"""
    def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,
                 ylim=None, xscale='linear', yscale='linear',
                 fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,
                 figsize=(3.5, 2.5)):
        # Çoklu çizgileri artarak çizdir
        if legend is None:
            legend = []
        d2l.use_svg_display()
        self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize)
        if nrows * ncols == 1:
            self.axes = [self.axes, ]
        # Argumanları elde tutmak için bir lambda işlevi kullan
        self.config_axes = lambda: d2l.set_axes(
            self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
        self.X, self.Y, self.fmts = None, None, fmts

    def add(self, x, y):
        # Çoklu veri noktalarını şekile ekleyin
        if not hasattr(y, "__len__"):
            y = [y]
        n = len(y)
        if not hasattr(x, "__len__"):
            x = [x] * n
        if not self.X:
            self.X = [[] for _ in range(n)]
        if not self.Y:
            self.Y = [[] for _ in range(n)]
        for i, (a, b) in enumerate(zip(x, y)):
            if a is not None and b is not None:
                self.X[i].append(a)
                self.Y[i].append(b)
        self.axes[0].cla()
        for x, y, fmt in zip(self.X, self.Y, self.fmts):
            self.axes[0].plot(x, y, fmt)
        self.config_axes()
        display.display(self.fig)
        display.clear_output(wait=True)

Aşağıdaki eğitim işlevi daha sonra, num_epochs ile belirtilen birden çok dönem için train_iter aracılığıyla erişilen bir eğitim veri kümesinde bir net modeli eğitir. Her dönemin sonunda model, test_iter aracılığıyla erişilen bir test veri kümesinde değerlendirilir. Eğitimin ilerlemesini görselleştirmek için Animator sınıfından yararlanacağız.

def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):  #@save
    """Bir modeli eğitin (Bölüm 3'te tanımlanmıştır)."""
    animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],
                        legend=['egitim kaybi', 'egitim dogr', 'test dogr'])
    for epoch in range(num_epochs):
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
        test_acc = evaluate_accuracy(net, test_iter)
        animator.add(epoch + 1, train_metrics + (test_acc,))
    train_loss, train_acc = train_metrics
    assert train_loss < 0.5, train_loss
    assert train_acc <= 1 and train_acc > 0.7, train_acc
    assert test_acc <= 1 and test_acc > 0.7, test_acc

Sıfırdan bir uygulama olarak, modelin kayıp fonksiyonunu 0.1 öğrenme oranıyla optimize ederek Section 3.2 içinde tanımlanan minigrup rasgele gradyan inişini kullanıyoruz.

lr = 0.1

def updater(batch_size):
    return d2l.sgd([W, b], lr, batch_size)
lr = 0.1

def updater(batch_size):
    return d2l.sgd([W, b], lr, batch_size)
class Updater():  #@save
    """Minigrup rasgele gradyan inişini kullanarak parametreleri güncellemek için."""
    def __init__(self, params, lr):
        self.params = params
        self.lr = lr

    def __call__(self, batch_size, grads):
        d2l.sgd(self.params, grads, self.lr, batch_size)

updater = Updater([W, b], lr=0.1)

Şimdi modeli 10 dönem ile eğitiyoruz. Hem dönem sayısının (num_epochs) hem de öğrenme oranının (lr) ayarlanabilir hiper parametreler olduğuna dikkat edin. Değerlerini değiştirerek modelin sınıflandırma doğruluğunu artırabiliriz.

num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)
../_images/output_softmax-regression-scratch_a48321_177_0.svg
num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)
../_images/output_softmax-regression-scratch_a48321_180_0.svg
num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)
../_images/output_softmax-regression-scratch_a48321_183_0.svg

3.6.7. Tahminleme

Artık eğitim tamamlandı, modelimiz bazı imgeleri sınıflandırmaya hazır. Bir dizi resim verildiğinde, bunların gerçek etiketlerini (metin çıktısının ilk satırı) ve modelden gelen tahminleri (metin çıktısının ikinci satırı) karşılaştıracağız.

def predict_ch3(net, test_iter, n=6):  #@save
    """Etiketleri tahmin etme (Bölüm 3'te tanımlanmıştır)."""
    for X, y in test_iter:
        break
    trues = d2l.get_fashion_mnist_labels(y)
    preds = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1))
    titles = [true +'\n' + pred for true, pred in zip(trues, preds)]
    d2l.show_images(
        X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n])

predict_ch3(net, test_iter)
../_images/output_softmax-regression-scratch_a48321_189_0.svg
def predict_ch3(net, test_iter, n=6):  #@save
    """Etiketleri tahmin etme (Bölüm 3'te tanımlanmıştır)."""
    for X, y in test_iter:
        break
    trues = d2l.get_fashion_mnist_labels(y)
    preds = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1))
    titles = [true +'\n' + pred for true, pred in zip(trues, preds)]
    d2l.show_images(
        X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n])

predict_ch3(net, test_iter)
../_images/output_softmax-regression-scratch_a48321_192_0.svg
def predict_ch3(net, test_iter, n=6):  #@save
    """Etiketleri tahmin etme (Bölüm 3'te tanımlanmıştır)."""
    for X, y in test_iter:
        break
    trues = d2l.get_fashion_mnist_labels(y)
    preds = d2l.get_fashion_mnist_labels(tf.argmax(net(X), axis=1))
    titles = [true +'\n' + pred for true, pred in zip(trues, preds)]
    d2l.show_images(
        tf.reshape(X[0:n], (n, 28, 28)), 1, n, titles=titles[0:n])

predict_ch3(net, test_iter)
../_images/output_softmax-regression-scratch_a48321_195_0.svg

3.6.8. Özet

  • Softmaks regresyonu ile çok sınıflı sınıflandırma için modeller eğitebiliriz.

  • Softmaks regresyonunun eğitim döngüsü doğrusal regresyondakine çok benzer: Verileri alın ve okuyun, modelleri ve kayıp fonksiyonlarını tanımlayın, ardından optimizasyon algoritmalarını kullanarak modelleri eğitin. Yakında öğreneceğiniz gibi, en yaygın derin öğrenme modellerinin benzer eğitim yordamları vardır.

3.6.9. Alıştırmalar

  1. Bu bölümde, softmaks işlemini matematiksel tanımına dayalı olarak doğrudan uyguladık. Bu hangi sorunlara neden olabilir? İpucu: \(\exp(50)\)’nin boyutunu hesaplamaya çalışın.

  2. Bu bölümdeki cross_entropy işlevi, çapraz entropi kaybı işlevinin tanımına göre uygulandı. Bu uygulamadaki sorun ne olabilir? İpucu: Logaritmanın etki alanını düşünün.

  3. Yukarıdaki iki sorunu çözmek için düşünebileceğiniz çözümler nelerdir?

  4. En olası etiketi iade etmek her zaman iyi bir fikir midir? Örneğin, bunu tıbbi teşhis için yapar mıydınız?

  5. Bazı özniteliklere dayanarak sonraki kelimeyi tahmin etmek için softmaks regresyonunu kullanmak istediğimizi varsayalım. Geniş bir kelime dağarcığı kullanımından ortaya çıkabilecek problemler nelerdir?