Jovian
⭐️
Sign In
In [2]:
# Imports
import torch
import torchvision
from torchvision.datasets import MNIST
# Download training dataset
dataset = MNIST(root='data/', download=True)

Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to data/MNIST/raw/train-images-idx3-ubyte.gz
100.1%
Extracting data/MNIST/raw/train-images-idx3-ubyte.gz Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz to data/MNIST/raw/train-labels-idx1-ubyte.gz
113.5%
Extracting data/MNIST/raw/train-labels-idx1-ubyte.gz Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz to data/MNIST/raw/t10k-images-idx3-ubyte.gz
100.4%
Extracting data/MNIST/raw/t10k-images-idx3-ubyte.gz Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz to data/MNIST/raw/t10k-labels-idx1-ubyte.gz
180.4%
Extracting data/MNIST/raw/t10k-labels-idx1-ubyte.gz Processing... Done!
In [3]:
len(dataset)

Out[3]:
60000
In [12]:
test_dataset = MNIST(root='data/', train=False)
len(test_dataset)
import matplotlib.pyplot as plt
%matplotlib inline
image, label = dataset[10]
img=image.convert("1")
plt.imshow(img)
print('Label:', label)

('Label:', 3)
Notebook Image
In [13]:
import torchvision.transforms as transforms
# MNIST dataset (images and labels)
dataset = MNIST(root='data/', 
                train=True,
                transform=transforms.ToTensor())
img_tensor, label = dataset[0]
print(img_tensor.shape, label)
(torch.Size([1, 28, 28]), 5)
In [14]:
print(img_tensor[:,10:15,10:15])
print(torch.max(img_tensor), torch.min(img_tensor))

tensor([[[0.0039, 0.6039, 0.9922, 0.3529, 0.0000], [0.0000, 0.5451, 0.9922, 0.7451, 0.0078], [0.0000, 0.0431, 0.7451, 0.9922, 0.2745], [0.0000, 0.0000, 0.1373, 0.9451, 0.8824], [0.0000, 0.0000, 0.0000, 0.3176, 0.9412]]]) (tensor(1.), tensor(0.))
In [15]:
plt.imshow(img_tensor[0], cmap='gray');

Notebook Image
In [1]:
from torch.utils.data.sampler import SubsetRandomSampler
from torch.utils.data.dataloader import DataLoader
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import torchvision.transforms as transforms
import torch
import torchvision
from torchvision.datasets import MNIST



batch_size=100

input_size = 28*28
num_classes = 10

# MNIST dataset (images and labels)
dataset = MNIST(root='data/', 
                train=True,
                transform=transforms.ToTensor())
img_tensor, label = dataset[0]


#随机分配函数
def split_indices(n, val_pct):
    # Determine size of validation set
    n_val = int(val_pct*n)
    # Create random permutation of 0 to n-1
    idxs = np.random.permutation(n)
    # Pick first n_val indices for validation set
    return idxs[n_val:], idxs[:n_val]

#产生训练和交叉验证集合的索引
train_indices, val_indices = split_indices(len(dataset), val_pct=0.2)
print(len(train_indices), len(val_indices))
print('Sample val indices: ', val_indices[:20])


# Training sampler and data loader
train_sampler = SubsetRandomSampler(train_indices)
print train_sampler
#数据加载
train_loader = DataLoader(dataset, 
                          batch_size, 
                          sampler=train_sampler)

# Validation sampler and data loader
val_sampler = SubsetRandomSampler(val_indices)
val_loader = DataLoader(dataset,
                        batch_size, 
                        sampler=val_sampler)


 
class MnistModel(nn.Module):
     
    def __init__(self):        
        super(MnistModel, self).__init__()
        #线性方程,<线性回归方程式> 
        self.linear = nn.Linear(input_size, num_classes)
    '''
    默认前向调用 需要改变张量形状
    我们的图像的形状是 1×28×28,但我们需要它们是大小为 784的向量,也就是说我们需要将其展平。
    我们将使用张量的 .reshape方法,这让我们可以有效地将每张图像「看作是」展平的向量,
    同时又不会真正改变底层数据。
    xb.reshape(-1, 28*28) 的意思是我们想要 xb 张量的二维视图,其中沿第二个维度的长度是28×28(即 784)。
    .reshape 的一个参数可以设置为 -1(在这里是第一个维度),以让PyTorch 根据原始张量的形状自动找到它。
    '''
    def forward(self, xb):
        xb = xb.reshape(-1, 784)
        out = self.linear(xb)
        return out
    
model = MnistModel()
print("model.linear.weight.shape: ", model.linear.weight.shape)
print("model.linear.bias.shape: ",model.linear.bias.shape)
for images, labels in train_loader:
    outputs = model(images)
    print('outputs.shape : ', outputs.shape)
    # Apply softmax for each output row
    #我的注释:outputs的第2个数表示需要分的类别数,这里是10个类别,
    #输出尺寸为('outputs.shape : ', torch.Size([100, 10]))
    #对应于caffe-protext文件最后的pool层。probs对应caffe里的prob前半部分
    '''
    layer {
	bottom: "pool5"
	top: "fc10"
	name: "fc10"
	type: "InnerProduct"
	inner_product_param {
    #表面最后输出10个类别数
		num_output: 10
		weight_filler {
      		  type: "xavier"
    		}
    		bias_filler {
      		  type: "constant"
      		  value: 0
    	        }
	}
}

layer {
	bottom: "fc10"
	bottom: "label"
	top: "prob"
	name: "prob"
    #计算loss的方法
	type: "SoftmaxWithLoss"
	include {
	  phase: TRAIN
	}
}
    '''
    probs = F.softmax(outputs, dim=1)
    print('probs.shape : ', probs.shape)
    # Look at sample probabilities
    print("Sample probabilities:\n", probs[:2].data)

    # Add up the probabilities of an output row
    print("Sum: ", torch.sum(probs[0]).item())
    break

print('outputs.shape : ', outputs.shape)
print('Sample outputs :\n', outputs[:2].data) 

#最后,我们只需选择每个输出行中概率最高的元素的索引,确定每张图像的预测标签即可。这可使用torch.max 完成,它会返回沿张量的一个特定维度的最大元素和该最大元素的索引。


max_probs, preds = torch.max(probs, dim=1)
print("preds: ", preds)
print("labels: ",labels)
def accuracy(l1, l2):
    print("equal: ", l1==l2)
    print(torch.sum(l1 == l2) )
    return torch.sum(l1 == l2).float() / len(l2)
def accuracyMatrix(outputs, labels):
    _, preds = torch.max(outputs, dim=1)
    return torch.sum(preds == labels).float() / len(preds)


print("accuracy: ", accuracy(preds, labels))
#用交叉熵评估损失
loss_fn = F.cross_entropy

# Loss for current batch of data
loss = loss_fn(outputs, labels)
print("loss: ", loss)




#定义批量损失
def loss_batch(model, loss_func, xb, yb, opt=None, metric=None):
    # Calculate loss
    preds = model(xb)
    loss = loss_func(preds, yb)
                     
    if opt is not None:
        # Compute gradients
        loss.backward()
        # Update parameters             
        opt.step()
        # Reset gradients
        opt.zero_grad()
    
    metric_result = None
    if metric is not None:
        # Compute the metric
        metric_result = metric(preds, yb)
    
    return loss.item(), len(xb), metric_result

#交叉验证,计算loss
def evaluate(model, loss_fn, valid_dl, metric=None):
    with torch.no_grad():
        # Pass each batch through the model
        results = [loss_batch(model, loss_fn, xb, yb, metric=metric)
                   for xb,yb in valid_dl]
        # Separate losses, counts and metrics
        losses, nums, metrics = zip(*results)
        # Total size of the dataset
        total = np.sum(nums)
        # Avg. loss across batches 
        avg_loss = np.sum(np.multiply(losses, nums)) / total
        avg_metric = None
        if metric is not None:
            # Avg. of metric across batches
            avg_metric = np.sum(np.multiply(metrics, nums)) / total
    return avg_loss, total, avg_metric


val_loss, total, val_acc = evaluate(model, loss_fn, val_loader, metric=accuracyMatrix)
print('Loss: {:.4f}, Accuracy: {:.4f}'.format(val_loss, val_acc))

def fit(epochs, model, loss_fn, opt, train_dl, valid_dl, metric=None):
    for epoch in range(epochs):
        # Training
        for xb,yb in train_dl:
            loss,_,_ = loss_batch(model, loss_fn, xb, yb, opt)

        # Evaluation
        result = evaluate(model, loss_fn, valid_dl, metric)
        val_loss, total, val_metric = result
        
        # Print progress
        if metric is None:
            print('Epoch [{}/{}], Loss: {:.4f}'
                  .format(epoch+1, epochs, val_loss))
        else:
            print('Epoch [{}/{}], Loss: {:.4f}, {}: {:.4f}'
                  .format(epoch+1, epochs, val_loss, metric.__name__, val_metric))
            
# Redifine model and optimizer
model = MnistModel()
learning_rate=0.001
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

#最后一个参数传入计算准确率的函数accuracyMatrix
fit(5, model, F.cross_entropy, optimizer, train_loader, val_loader, accuracyMatrix)

 
(48000, 12000) ('Sample val indices: ', array([37159, 59803, 11354, 51971, 54966, 31265, 6667, 48202, 32095, 29185, 21032, 25192, 16161, 2058, 29027, 17640, 50824, 10998, 17560, 19281])) <torch.utils.data.sampler.SubsetRandomSampler object at 0x114da0e90> ('model.linear.weight.shape: ', torch.Size([10, 784])) ('model.linear.bias.shape: ', torch.Size([10])) ('outputs.shape : ', torch.Size([100, 10])) ('probs.shape : ', torch.Size([100, 10])) ('Sample probabilities:\n', tensor([[0.0874, 0.1024, 0.0981, 0.1002, 0.0878, 0.1407, 0.0976, 0.0974, 0.0923, 0.0961], [0.0969, 0.1017, 0.1002, 0.0899, 0.0990, 0.1389, 0.0766, 0.0979, 0.0807, 0.1182]])) ('Sum: ', 0.9999999403953552) ('outputs.shape : ', torch.Size([100, 10])) ('Sample outputs :\n', tensor([[-0.0968, 0.0610, 0.0185, 0.0395, -0.0924, 0.3792, 0.0129, 0.0114, -0.0430, -0.0017], [ 0.0267, 0.0753, 0.0600, -0.0488, 0.0481, 0.3866, -0.2085, 0.0371, -0.1563, 0.2249]])) ('preds: ', tensor([5, 5, 3, 0, 4, 4, 3, 9, 5, 3, 3, 3, 5, 5, 6, 4, 8, 4, 2, 5, 2, 5, 5, 5, 8, 1, 5, 3, 3, 3, 6, 5, 3, 3, 2, 4, 2, 3, 1, 9, 4, 4, 4, 3, 4, 5, 6, 4, 5, 5, 7, 5, 5, 3, 8, 6, 5, 5, 4, 5, 5, 3, 5, 4, 4, 0, 5, 1, 5, 2, 3, 5, 3, 3, 5, 9, 4, 5, 2, 1, 3, 4, 5, 7, 8, 4, 5, 3, 4, 7, 3, 5, 3, 5, 4, 5, 5, 3, 5, 2])) ('labels: ', tensor([9, 4, 4, 8, 2, 8, 0, 5, 7, 7, 8, 1, 7, 9, 1, 2, 7, 7, 3, 6, 2, 0, 4, 2, 7, 9, 6, 2, 1, 8, 0, 4, 2, 1, 0, 0, 3, 8, 4, 1, 5, 2, 4, 1, 3, 6, 6, 0, 6, 7, 3, 6, 4, 5, 0, 0, 9, 4, 4, 4, 6, 7, 4, 2, 3, 5, 4, 7, 7, 8, 5, 8, 1, 0, 4, 0, 4, 9, 5, 7, 5, 2, 6, 3, 2, 4, 9, 8, 4, 3, 8, 6, 2, 9, 4, 2, 5, 2, 9, 7])) ('equal: ', tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], dtype=torch.uint8)) tensor(9) ('accuracy: ', tensor(0.0900)) ('loss: ', tensor(2.3128, grad_fn=<NllLossBackward>)) Loss: 2.3166, Accuracy: 0.1042 Epoch [1/5], Loss: 1.9012, accuracyMatrix: 0.6492 Epoch [2/5], Loss: 1.5953, accuracyMatrix: 0.7469 Epoch [3/5], Loss: 1.3796, accuracyMatrix: 0.7822 Epoch [4/5], Loss: 1.2246, accuracyMatrix: 0.7993 Epoch [5/5], Loss: 1.1097, accuracyMatrix: 0.8133
In [2]:
fit(20, model, F.cross_entropy, optimizer, train_loader, val_loader, accuracyMatrix)
Epoch [1/20], Loss: 1.0219, accuracyMatrix: 0.8235 Epoch [2/20], Loss: 0.9529, accuracyMatrix: 0.8304 Epoch [3/20], Loss: 0.8972, accuracyMatrix: 0.8352 Epoch [4/20], Loss: 0.8514, accuracyMatrix: 0.8402 Epoch [5/20], Loss: 0.8130, accuracyMatrix: 0.8440 Epoch [6/20], Loss: 0.7804, accuracyMatrix: 0.8476 Epoch [7/20], Loss: 0.7522, accuracyMatrix: 0.8503 Epoch [8/20], Loss: 0.7275, accuracyMatrix: 0.8534 Epoch [9/20], Loss: 0.7058, accuracyMatrix: 0.8560 Epoch [10/20], Loss: 0.6867, accuracyMatrix: 0.8575 Epoch [11/20], Loss: 0.6694, accuracyMatrix: 0.8592 Epoch [12/20], Loss: 0.6538, accuracyMatrix: 0.8610 Epoch [13/20], Loss: 0.6398, accuracyMatrix: 0.8627 Epoch [14/20], Loss: 0.6270, accuracyMatrix: 0.8638 Epoch [15/20], Loss: 0.6152, accuracyMatrix: 0.8657 Epoch [16/20], Loss: 0.6044, accuracyMatrix: 0.8676 Epoch [17/20], Loss: 0.5944, accuracyMatrix: 0.8686 Epoch [18/20], Loss: 0.5852, accuracyMatrix: 0.8692 Epoch [19/20], Loss: 0.5766, accuracyMatrix: 0.8701 Epoch [20/20], Loss: 0.5686, accuracyMatrix: 0.8712
In [31]:
import matplotlib.pyplot as plt
%matplotlib inline
# Replace these values with your results
accuracies = [0.1076, 0.6486, 0.7362, 0.7737, 0.7925, 0.8069, 
              0.8165, 0.8227, 0.8269, 0.8325, 0.8367,
              0.8399, 0.8438, 0.8463, 0.8482, 0.8512,
              0.8529, 0.8552, 0.8563, 0.8587, 0.8606]
plt.plot(accuracies, '-x')
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.title('Accuracy vs. No. of epochs');
Notebook Image
In [4]:
#打印测试集里的图片
# Define test dataset
test_dataset = MNIST(root='data/', 
                     train=False,
                     transform=transforms.ToTensor())
img, label = test_dataset[0]
plt.imshow(img[0], cmap='gray')
print('Shape:', img.shape)
print('Label:', label)

NameErrorTraceback (most recent call last) <ipython-input-4-04365736488f> in <module>() 5 transform=transforms.ToTensor()) 6 img, label = test_dataset[0] ----> 7 plt.imshow(img[0], cmap='gray') 8 print('Shape:', img.shape) 9 print('Label:', label) NameError: name 'plt' is not defined
In [47]:
#前向预测
def predict_image(img, model):
    #img.unsqueeze 会直接在1x28x28 张量的起始处增加另一个维度,
    #使其成为一个 1×1×28×28 的张量,模型将其视为一个包含单张图像的批。
    #这个扩展不是必须的,但可用于批量处理的时候。
    xb = img.unsqueeze(0)  
    #print(xb.size())
    yb = model(xb)
    _, preds  = torch.max(yb, dim=1)
    return preds[0].item()
img, label = test_dataset[0]
plt.imshow(img[0], cmap='gray')
print('Label:', label, ', Predicted:', predict_image(img, model))
('Label:', 7, ', Predicted:', 8)
Notebook Image
In [6]:
#在整个测试集合上的准确率
test_loader = DataLoader(test_dataset, batch_size=200)

test_loss, total, test_acc = evaluate(model, loss_fn, test_loader, metric=accuracyMatrix)
print('Loss: {:.4f}, Accuracy: {:.4f}'.format(test_loss, test_acc))
Loss: 0.5515, Accuracy: 0.8736
In [7]:
#保存模型 将权重和偏置矩阵保存到磁盘。这里会保存到当前jupyter执行的目录下
torch.save(model.state_dict(), 'mnist-logistic.pth')
In [8]:
#.state_dict 方法会返回一个  OrderedDict,其中包含映射到该模型的适当属性的所有权重和偏置。
model.state_dict()
Out[8]:
OrderedDict([('linear.weight',
              tensor([[-1.4711e-02, -3.0804e-02, -2.8100e-02,  ..., -6.5806e-04,
                        2.1894e-03, -5.5807e-03],
                      [-3.3028e-03,  1.1784e-02,  2.9146e-02,  ..., -1.9377e-02,
                       -3.1632e-03, -5.6850e-03],
                      [ 1.5427e-03, -4.5229e-05, -1.0716e-02,  ..., -3.1243e-02,
                       -2.7873e-02, -2.0640e-02],
                      ...,
                      [ 1.9138e-02,  6.4322e-03,  6.9461e-03,  ...,  1.0216e-02,
                        1.1323e-02,  1.1745e-02],
                      [ 1.6693e-02, -1.9302e-02,  5.9756e-03,  ...,  3.4606e-02,
                       -1.1532e-02,  1.7906e-02],
                      [ 2.0796e-02,  3.3226e-02, -3.3144e-02,  ...,  2.3037e-02,
                       -1.9883e-03, -1.4581e-02]])),
             ('linear.bias',
              tensor([-0.0368,  0.1012, -0.0439, -0.0343,  0.0333,  0.0746, -0.0275,  0.0627,
                      -0.1484, -0.0419]))])
In [9]:
#要加载该模型的权重,我们可以实例化  MnistModel 类的一个新对象,并使用 .load_state_dict 方法。
model2 = MnistModel()
model2.load_state_dict(torch.load('mnist-logistic.pth'))
model2.state_dict()
Out[9]:
OrderedDict([('linear.weight',
              tensor([[-1.4711e-02, -3.0804e-02, -2.8100e-02,  ..., -6.5806e-04,
                        2.1894e-03, -5.5807e-03],
                      [-3.3028e-03,  1.1784e-02,  2.9146e-02,  ..., -1.9377e-02,
                       -3.1632e-03, -5.6850e-03],
                      [ 1.5427e-03, -4.5229e-05, -1.0716e-02,  ..., -3.1243e-02,
                       -2.7873e-02, -2.0640e-02],
                      ...,
                      [ 1.9138e-02,  6.4322e-03,  6.9461e-03,  ...,  1.0216e-02,
                        1.1323e-02,  1.1745e-02],
                      [ 1.6693e-02, -1.9302e-02,  5.9756e-03,  ...,  3.4606e-02,
                       -1.1532e-02,  1.7906e-02],
                      [ 2.0796e-02,  3.3226e-02, -3.3144e-02,  ...,  2.3037e-02,
                       -1.9883e-03, -1.4581e-02]])),
             ('linear.bias',
              tensor([-0.0368,  0.1012, -0.0439, -0.0343,  0.0333,  0.0746, -0.0275,  0.0627,
                      -0.1484, -0.0419]))])
In [10]:
#就像进行完整性检查一样,我们在测试集上验证一下该模型是否有与之前一样的损失和准确度。
test_loss, total, test_acc = evaluate(model, loss_fn, test_loader, metric=accuracyMatrix)
print('Loss: {:.4f}, Accuracy: {:.4f}'.format(test_loss, test_acc))
Loss: 0.5515, Accuracy: 0.8736
In [ ]:
import jovian
jovian.commit()
In [ ]: