<jupyter_start><jupyter_text>**Homework**1.   Get sudoku dataset from: [kaggle](https://www.kaggle.com/bryanpark/sudoku)2.   Train a Sudoku Solver with *linear layers* , in Pytorch :)Of course, for the full mark on this homework you aren't expected to achieve state-of-the-art performance with this model.You should:*   Get data in pytorch-friendly format.*   Formulate a loss function for this problem.*   Create a model that is able to learn with the proposed loss function.*   Observe train/validation error; train-error should steadly decrease*   (optional) Wire the network in a way that makes learning easier.<jupyter_code>import torch
if torch.cuda.is_available():
    device = torch.device('cuda')
else:
    device = torch.device('cpu')
from google.colab import drive
drive.mount('/content/gdrive')
FILE_PATH ='/content/gdrive/My Drive/Colab Notebooks/Homework#1/sudoku.csv'
#read sudoku.csv
import numpy as np
quizzes = np.zeros((1000000, 81), np.int32)
solutions = np.zeros((1000000, 81), np.int32)
for i, line in enumerate(open(FILE_PATH, 'r').read().splitlines()[1:]):
  quiz, solution = line.split(",")
  for j, q_s in enumerate(zip(quiz, solution)):
    q, s = q_s
    quizzes[i, j] = int(q)
    solutions[i, j] = int(s)
quizzes = quizzes.reshape((-1, 9, 9))
solutions = solutions.reshape((-1, 9, 9))
np.save('/content/gdrive/My Drive/Colab Notebooks/Homework#1/quizzes.npy', quizzes)
np.save('/content/gdrive/My Drive/Colab Notebooks/Homework#1/solutions.npy', solutions)
quizzes = np.load('/content/gdrive/My Drive/Colab Notebooks/Homework#1/quizzes.npy')
solutions = np.load('/content/gdrive/My Drive/Colab Notebooks/Homework#1/solutions.npy')
quizzes = torch.Tensor(quizzes).cuda()
solutions = torch.Tensor(solutions).cuda()
print(quizzes)
print(solutions)
print(quizzes.shape)
print(solutions.shape)
quizzesVetorize = torch.squeeze(quizzes.reshape((-1, 81, 1)))
solutionsVetorize = torch.squeeze(solutions.reshape((-1, 81, 1)))
print(quizzesVetorize.shape)
print(solutionsVetorize.shape)
randPerm = torch.randperm((quizzesVetorize.shape)[0])
quizzesVetorizeRand = quizzesVetorize[randPerm][:]
solutionsVetorizeRand = solutionsVetorize[randPerm][:]
import math

train_size = math.floor(0.3 * (quizzesVetorizeRand.shape)[0])
val_size = math.floor(0.05 * (quizzesVetorizeRand.shape)[0])
test_size = math.floor(0.01 * (quizzesVetorizeRand.shape)[0])
train_data = quizzesVetorizeRand[0:train_size][:]
train_labels = solutionsVetorizeRand[0:train_size][:]
val_data = quizzesVetorizeRand[train_size:train_size + val_size][:]
val_labels = solutionsVetorizeRand[train_size:train_size + val_size][:]
test_data = quizzesVetorizeRand[train_size + val_size : train_size + val_size + test_size][:]
test_labels = solutionsVetorizeRand[train_size + val_size : train_size + val_size + test_size][:]
print(train_size)
print(val_size)
print(test_size)
print(train_data.shape)
print(train_labels.shape)
print(val_data.shape)
print(val_labels.shape)
print(test_data.shape)
print(test_labels.shape)
train_data = train_data.cuda()
train_labels = train_labels.cuda()
val_data = val_data.cuda()
val_labels = val_labels.cuda()
test_data = test_data.cuda()
test_labels = test_labels.cuda()
import matplotlib.pyplot as plt

def plot_loss(loss, label, color='blue'):
    plt.plot(loss, label=label, color=color)
    plt.legend()
import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    # nn.Module - base class for all models
    # easy acces to .parameters() and .zero_grad()
    # define .forward() instead of __call__()
    
    def __init__(self, in_size, h_size1, h_size2, out_size):
        super().__init__()
        self._layer1 = nn.Linear(in_size,h_size1).cuda()
        self._layer2 = nn.Linear(h_size1,h_size2).cuda()
        self._layer3 = nn.Linear(h_size2,out_size).cuda()
        
    def forward(self, x):
        x = F.relu(self._layer1(x)).cuda()
        x = F.relu(self._layer2(x)).cuda()
        x = self._layer3(x).cuda()
        return x.cuda()
        
    def train(self, train_data, train_labels, 
              epochs=400, lr=0.01, verbose=100, l2_weight=0,
              val_data=None, val_labels=None):
        #use optimizers to take care of the update step
        optimizer1 = torch.optim.SGD(self.parameters(), lr=lr,
                               weight_decay = l2_weight)
        
        optimizer2 = torch.optim.Adagrad(self.parameters())
        optimizer3 = torch.optim.RMSprop(self.parameters())
        optimizer4 = torch.optim.Adam(self.parameters())
        
        criterion = nn.CrossEntropyLoss();
        
        train_loss = []
        val_loss = []
        for e in range(epochs):
            optimizer4.zero_grad()   # zero the gradient buffers
            input = train_data
            output = self(train_data).reshape(-1,9,81)
            loss = criterion(output, train_labels)
            loss.backward()
            optimizer4.step()    # Does the update
            
            train_loss.append(loss.cpu().detach().numpy())
            if verbose!=0 and e%verbose==0:
                print("Loss - train :")
                print(loss)
            if val_data is not None:
                output = self(val_data).reshape(-1,9,81)
                loss = criterion(output, val_labels)
                if verbose!=0 and e%verbose==0:
                    print("Loss - validation :")
                    print(loss)
                val_loss.append(loss.cpu().detach().numpy())
            
        plot_loss(train_loss, 'train-loss')
        if len(val_loss)>0:
            plot_loss(val_loss, 'val-loss', color='red')
#define net
net = Net(81,128, 128, 81*9)
net.to(device)
print(net)
#train from one example from train set
input = train_data[0:2][:] - 1
print(input.shape)
output = net(input.type(torch.float)).reshape(-1, 9, 81)
print(output.shape)
target = train_labels[0:2][:] - 1
print(target.shape)

criterion = nn.CrossEntropyLoss()

loss = criterion(output, target.type(torch.long))
print(loss)
#train from all examples from train set
net.train((train_data-1).type(torch.float), (train_labels-1).type(torch.long), 
        epochs=1000,lr=0.1,verbose=100,
        val_data = (val_data-1).type(torch.float), 
        val_labels=(val_labels-1).type(torch.long)) 
input = test_data[1][:]
print(input.reshape(9,9))
output = net(input.type(torch.float)).reshape(81, 9)
print(output.max(1)[1].reshape(9,9) + 1)
target = test_labels[1][:]
print(target.reshape(9,9))
bad = 0
for i in range(test_size):
  if net(test_data[i].type(torch.float))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13798899755501223, 'fraction_numerical': 0.022921760391198046, 'mean_word_length': 3.6319886765746636, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 6, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '554518', 'license': 'MIT', 'n_tokens_mistral': 2397, 'n_tokens_neox': 2293, 'n_words': 452, 'path': 'Bachelors Year 3/Deep Learning/Sudoku Solver/Homework#1.ipynb', 'repo_name': 'marianlupascu/School-Projects', 'size': '6544'}