Having problems with Pandas when storing the results of a CNN
I have a CNN that runs well, but when I'm trying to store the error, loss and accuracy training and validation with Pandas, for some reason the Data Frame that I created has more rows than necesary (173 to be exact) and it looks like it trains more than I ask to, but while the CNN is training and validating the results it gives are according to what I ask of it. I will lay all of my code here by parts.
This is how I define my neural network
class Network(nn.Module):
    def __init__(self, p=0.1):
        super().__init__()

        # define layers
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5)
        self.conv2 = nn.Conv2d(in_channels=6, out_channels=12, kernel_size=5)

        self.fc1 = nn.Linear(in_features=12 * 4 * 4, out_features=120)
        self.fc2 = nn.Linear(in_features=120, out_features=60)
        self.out = nn.Linear(in_features=60, out_features=10)

        self.dropout = nn.Dropout(p)

    # define forward function
    def forward(self, t):
        # conv 1
        t = self.conv1(t)
        t = F.relu(t)
        t = F.max_pool2d(t, kernel_size=2, stride=2)

        # conv 2
        t = self.conv2(t)
        t = F.relu(t)
        t = F.max_pool2d(t, kernel_size=2, stride=2)

        # fc1
        t = t.reshape(-1, 12 * 4 * 4)
        t = self.fc1(t)
        t = self.dropout(t)
        t = F.relu(t)

        # fc2
        t = self.fc2(t)
        t = self.dropout(t)
        t = F.relu(t)

        # output
        t = self.out(t)
        # don't need softmax here since we'll use cross-entropy as activation.

        return t

I call my network as my model and move it to the CPU
model = Network().to(device)

This is how I define my test and training loops
def train_loop(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)

    for batch, (X, y) in enumerate(dataloader):
        
        pred = model(X)
        loss = loss_fn(pred, y)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if batch % 100 == 0:
            loss, current = loss.item(), batch * len(X)
            

def test_loop(dataloader,model,loss_fn):
    num_samples  = 0
    num_batches  = 0
    avrg_loss    = 0
    frac_correct = 0
    model.eval()
      
    model = model.to(device)    
    
    with torch.no_grad():
   
        for X,y in dataloader:
            X = X.to(device)
            y = y.to(device)           
            pred = model(X)
            num_batches += 1
            avrg_loss += loss_fn(pred,y).item()
            num_samples += y.size(0)            
            frac_correct += (pred.argmax(1)==y).type(torch.float).sum().item()
   
    avrg_loss    /= num_batches
    frac_correct /= num_samples
    return avrg_loss,frac_correct

And here is where I call my CNN and start training it
learning_rate = 1e-3
batch_size = 1000
num_epochs = 100
num_k = 1 
n=10 
dropouts=[0.1,0.3,0.5]

loss_fn = nn.CrossEntropyLoss()

df = pd.DataFrame()

for p in dropouts:
    model = Network(p)
    train_dataloader = DataLoader(train_dataset,batch_size=batch_size)
    valid_dataloader = DataLoader(test_dataset,batch_size=batch_size)         
    optimizer = torch.optim.Adam(model.parameters(),lr=learning_rate,eps=1e-08,weight_decay=0,amsgrad=False)
    min_valid_loss = float(&quot;inf&quot;)
    for epoch in range(num_epochs):
        train_loop(train_dataloader,model,loss_fn,optimizer)
        train_loss,train_accu = test_loop(train_dataloader,model,loss_fn)
        valid_loss,valid_accu = test_loop(valid_dataloader,model,loss_fn)
        print(f&quot;n={n} p={p} epoch={epoch} train_loss={train_loss} train_accu={train_accu} valid_loss={valid_loss} valid_accu={valid_accu}&quot;)
        df = df.append({&quot;n&quot;:n,
                        &quot;p&quot;:p,
                        &quot;epoch&quot;:epoch,
                        &quot;train_loss&quot;:train_loss,
                        &quot;train_accu&quot;:train_accu,
                        &quot;valid_loss&quot;:valid_loss,
                        &quot;valid_accu&quot;:valid_accu}
                        ,ignore_index=True)
json_fname = &quot;simulation-results-&quot;+datetime.datetime.now().strftime(&quot;%Y-%m-%d-%H-%M-%S&quot;)+&quot;.json&quot;
df.to_json(json_fname)
if COLAB:
    files.download(json_fname)

The results I get while is training look like this
n=10 p=0.1 epoch=0 train_loss=0.8500287334124247 train_accu=0.6877666666666666 valid_loss=0.864807802438736 valid_accu=0.684

so I can sort of infere that it's doing a good job. But when I ask Pandas ifnormation about the DataFrame df with df.info() I get this
&lt;class 'pandas.core.frame.DataFrame'&gt;
RangeIndex: 474 entries, 0 to 473
Data columns (total 8 columns):
 #   Column      Non-Null Count  Dtype  
---  ------      --------------  -----  
 0   n           425 non-null    float64
 1   k           174 non-null    float64
 2   epoch       474 non-null    int64  
 3   train_loss  474 non-null    float64
 4   train_accu  474 non-null    float64
 5   valid_loss  474 non-null    float64
 6   valid_accu  474 non-null    float64
 7   p           300 non-null    float64
dtypes: float64(7), int64(1)
memory usage: 29.8 KB

So Pandas is,for some reason, adding 173 rows with information that makes no sense (for instance my dropout p is NaN in those 173 rows) and I don't know what's going on.

Hmm, you should try wrapping your dictionary with brackets like so,
data = {'n': n, 'n': p, 'epoch': epoch} # etc...
pd.DataFrame([data])

If this doesn't work, you should consider converting your dictionary to a pandas dataframe using this function.
data = {'n': n, 'p': p, 'epoch': epoch} # etc...
pd.DataFrame.from_dict(data)

You can also specify your own columns to make the dataframe more readable.
data = {'n': n, 'p': p, 'epoch': epoch} # etc...
columns = ['N', 'P', 'Epoch'] # etc...
pd.DataFrame.from_dict(data, columns=columns)

Obviously, the column size must match the data.
