{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd#导入csv文件的库\nimport math\nimport numpy as np#进行矩阵运算的库\nimport matplotlib.pyplot as plt#作图的库\nimport torch#一个深度学习的库Pytorch\nimport torch.nn as nn#neural network,神经网络\nimport torch.optim as optim#一个实现了各种优化算法的库\nimport torch.nn.functional as F#神经网络函数库\nimport warnings#避免一些可以忽略的报错\nwarnings.filterwarnings('ignore')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-07-10T04:01:21.935188Z","iopub.execute_input":"2023-07-10T04:01:21.935480Z","iopub.status.idle":"2023-07-10T04:01:25.490520Z","shell.execute_reply.started":"2023-07-10T04:01:21.935437Z","shell.execute_reply":"2023-07-10T04:01:25.489532Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#设置随机种子\nimport random\ntorch.backends.cudnn.deterministic = True#将cudnn框架中的随机数生成器设为确定性模式\ntorch.backends.cudnn.benchmark = False#关闭CuDNN框架的自动寻找最优卷积算法的功能，以避免不同的算法对结果产生影响\ntorch.manual_seed(42)\nnp.random.seed(42)\nrandom.seed(42)","metadata":{"execution":{"iopub.status.busy":"2023-07-10T04:01:25.492595Z","iopub.execute_input":"2023-07-10T04:01:25.493141Z","iopub.status.idle":"2023-07-10T04:01:25.502207Z","shell.execute_reply.started":"2023-07-10T04:01:25.493112Z","shell.execute_reply":"2023-07-10T04:01:25.501279Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train=pd.read_csv(\"/kaggle/input/cifar-10dataset/CIFAR-10/train.csv\")\ntrain.head()","metadata":{"execution":{"iopub.status.busy":"2023-07-10T04:01:25.503671Z","iopub.execute_input":"2023-07-10T04:01:25.504269Z","iopub.status.idle":"2023-07-10T04:01:55.191659Z","shell.execute_reply.started":"2023-07-10T04:01:25.504234Z","shell.execute_reply":"2023-07-10T04:01:55.190519Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"datay=train['label'].values\ntrain=train.drop(['Unnamed: 0','label',],axis=1)\nX=train.values.reshape(-1,3,32,32).transpose(0,2,3,1)\nprint(f\"X.shape;{X.shape}\")","metadata":{"execution":{"iopub.status.busy":"2023-07-10T04:01:55.194158Z","iopub.execute_input":"2023-07-10T04:01:55.195051Z","iopub.status.idle":"2023-07-10T04:01:55.552209Z","shell.execute_reply.started":"2023-07-10T04:01:55.195012Z","shell.execute_reply":"2023-07-10T04:01:55.551038Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import skimage.io#用于读取和保存图像文件\nimport skimage.feature#包含用于图像特征提取的函数和方法\nimport skimage.color#用于颜色空间转换和处理的函数和方法\nimport skimage.transform#包含图像变换和几何操作的函数和方法\nimport skimage.util#包含用于图像处理的各种实用函数\nimport skimage.segmentation#用于图像分割算法的函数和方法\n\n\ndef _generate_segments(im_orig, scale, sigma, min_size):\n\n    #skimage.util.img_as_float:将像素值转成浮点类型,并映射到[0,1]的区间内\n    #Felzenszwalb超像素分割算法,根据图像中的颜色和纹理信息将图像划分为具有相似属性的区域.\n    im_mask = skimage.segmentation.felzenszwalb(\n        skimage.util.img_as_float(im_orig), scale=scale, sigma=sigma,\n        min_size=min_size)\n    \n    #将im_mask拼接到3个颜色通道的后面,形成第4个通道.\n    im_orig = np.append(im_orig, np.zeros(im_orig.shape[:2])[:, :, np.newaxis], axis=2)\n    im_orig[:, :, 3] = im_mask\n\n    return im_orig","metadata":{"execution":{"iopub.status.busy":"2023-07-10T04:01:55.553813Z","iopub.execute_input":"2023-07-10T04:01:55.554820Z","iopub.status.idle":"2023-07-10T04:01:57.285701Z","shell.execute_reply.started":"2023-07-10T04:01:55.554781Z","shell.execute_reply":"2023-07-10T04:01:57.284729Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataX=[]\nfor index in range(len(X)):\n    dataX.append(_generate_segments(X[index], scale=1.0, sigma=0.8, min_size=50))\ndataX=np.array(dataX).transpose(0,3,1,2)\nprint(f\"dataX.shape:{dataX.shape}\")","metadata":{"execution":{"iopub.status.busy":"2023-07-10T04:01:57.287038Z","iopub.execute_input":"2023-07-10T04:01:57.287382Z","iopub.status.idle":"2023-07-10T04:03:19.126384Z","shell.execute_reply.started":"2023-07-10T04:01:57.287353Z","shell.execute_reply":"2023-07-10T04:03:19.125337Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#划分训练集和测试集的函数\ndef train_test_split(dataX,datay,shuffle=True,percentage=0.8):\n    \"\"\"\n    将训练数据X和标签y以numpy.array数组的形式传入\n    划分的比例定为训练集:测试集=8:2\n    \"\"\"\n    if shuffle:\n        random_num=[index for index in range(len(dataX))]\n        np.random.shuffle(random_num)\n        dataX=dataX[random_num]\n        datay=datay[random_num]\n    split_num=int(len(dataX)*percentage)\n    train_X=dataX[:split_num]\n    train_y=datay[:split_num]\n    test_X=dataX[split_num:]\n    test_y=datay[split_num:]\n    return train_X,train_y,test_X,test_y","metadata":{"execution":{"iopub.status.busy":"2023-07-10T04:03:19.127734Z","iopub.execute_input":"2023-07-10T04:03:19.128098Z","iopub.status.idle":"2023-07-10T04:03:19.135534Z","shell.execute_reply.started":"2023-07-10T04:03:19.128065Z","shell.execute_reply":"2023-07-10T04:03:19.134625Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_X,train_y,valid_X,valid_y=train_test_split(dataX,datay,percentage=0.9)\nlabel_count=np.max(train_y)+1\nfor index in range(label_count):\n    print(f\"np.sum(train_y==index)={np.sum(train_y==index)}\")\nprint(f\"train_X.shape:{train_X.shape},valid_X.shape:{valid_X.shape}\")","metadata":{"execution":{"iopub.status.busy":"2023-07-10T04:03:19.136937Z","iopub.execute_input":"2023-07-10T04:03:19.137503Z","iopub.status.idle":"2023-07-10T04:03:19.647233Z","shell.execute_reply.started":"2023-07-10T04:03:19.137470Z","shell.execute_reply":"2023-07-10T04:03:19.646208Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"\n图片的数据增强操作:\n随机水平翻转,竖直翻转,随机掩盖一些区域,图像锐化,给图像加噪音.\n\"\"\"\ndef image_Augmentation(image,horizontalflip_factor=0.025,verticalflip_factor=0.025,\n                       mask_factor=0.005,is_sharpening=False,noise_factor=0.025):\n    \"\"\"\n    input:image(numpy.array):3*32*32(channels*height*width)\n    resize_height*resize_width是调整后的图像大小\n    horizontalflip_factor是水平翻转的概率\n    verticalflip_factor是垂直翻转的概率\n    mask_factor是随机掩盖的概率\n    is_sharpening是是否选择锐化\n    noise_factor:噪音因子\n    \"\"\"\n    channels,image_height,image_width=image.shape\n\n    #如果生成的随机数小于水平翻转的概率,就对数组进行水平翻转\n    if np.random.random()<horizontalflip_factor:\n        image=np.flip(image, axis=1)\n    #如果生成的随机数小于垂直翻转的概率,就对数组进行竖直翻转\n    if np.random.random()<verticalflip_factor:\n        image=np.flip(image, axis=0)\n\n    #选择需要掩盖的区域\n    mask=np.random.rand(channels,image_height,image_width)>mask_factor\n    image=image*mask\n\n    #对图像进行锐化操作\n    if is_sharpening:\n        pad_size=((0,0),(1,1),(1,1))#通道数,上下左右padding\n        image=np.pad(image,pad_size,mode=\"constant\",constant_values=0)#用常数0来进行填充,34*34*3\n        \n        #拉普拉斯算子\n        kernel=np.array([[0,-1,0],[-1,4,-1],[0,-1,0]])\n        \n        #对图像进行卷积操作\n        new_image=[]\n        for i in range(image_height):\n            for j in range(image_width):\n                for k in range(channels):\n                    new_image.append( np.sum(image[k,i:i+3,j:j+3]*kernel)   )\n                    \n        image=np.array(new_image).reshape(channels,image_height,image_width)#3*32*32\n    \n    #给图像加上适当的噪音\n    noise=np.random.randn(channels,image_height,image_width)*noise_factor#生成满足高斯分布的噪音\n    image+=noise\n\n    #返回的是经过数据增强之后的图片\n    return image","metadata":{"execution":{"iopub.status.busy":"2023-07-10T04:03:19.649141Z","iopub.execute_input":"2023-07-10T04:03:19.649538Z","iopub.status.idle":"2023-07-10T04:03:19.662341Z","shell.execute_reply.started":"2023-07-10T04:03:19.649504Z","shell.execute_reply":"2023-07-10T04:03:19.661131Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#多头注意力机制\nclass MultiHeadSelfAttention(nn.Module):\n    #定义初始化函数,dim_in是\n    def __init__(self,dim_in,d_model,num_heads=4):\n\n        super(MultiHeadSelfAttention,self).__init__()\n        \n        self.dim_in=dim_in\n        \n        self.d_model=d_model\n        \n        self.num_heads=num_heads\n        \n        #向量的维度必须被头的个数整除,否则会抛出异常.\n        assert d_model %num_heads==0,\"d_model must be multiple of num_heads\"\n        \n        #定义线性变换矩阵\n        self.linear_q=nn.Linear(dim_in,d_model)\n        self.linear_k=nn.Linear(dim_in,d_model)\n        self.linear_v=nn.Linear(dim_in,d_model)\n        self.scale=1/math.sqrt(d_model//num_heads)\n        \n        #最后的线性层\n        self.fc=nn.Linear(d_model,d_model)\n        \n    def forward(self,x):\n        batch,n,dim_in=x.shape\n        \n        assert dim_in==self.dim_in\n        \n        nh=self.num_heads\n        \n        dk=self.d_model//nh\n        \n        q=self.linear_q(x).reshape(batch,n,nh,dk).transpose(1,2)\n        k=self.linear_k(x).reshape(batch,n,nh,dk).transpose(1,2)\n        v=self.linear_v(x).reshape(batch,n,nh,dk).transpose(1,2)\n        \n        \n        dist=torch.matmul(q,k.transpose(2,3))*self.scale\n        dist=torch.softmax(dist,dim=-1)\n        \n        att=torch.matmul(dist,v)\n        \n        att=att.transpose(1,2).reshape(batch,n,self.d_model)\n        \n        output=self.fc(att)\n        \n        return output  ","metadata":{"execution":{"iopub.status.busy":"2023-07-10T04:03:19.666824Z","iopub.execute_input":"2023-07-10T04:03:19.667239Z","iopub.status.idle":"2023-07-10T04:03:19.679283Z","shell.execute_reply.started":"2023-07-10T04:03:19.667203Z","shell.execute_reply":"2023-07-10T04:03:19.678200Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#对特征图的每个位置也提取一次权重\nclass SEConv(nn.Module):\n    def __init__(self,area=512,reduction=16):\n        super(SEConv,self).__init__()\n        self.fc=nn.Sequential(\n            nn.Linear(area,area//reduction,bias=True),\n            nn.GELU(),\n            nn.Linear(area//reduction,area,bias=True),\n            nn.Sigmoid(),\n        )\n    def forward(self,x):\n        #batch_size,channels,(height*width)\n        b,c,area=x.shape\n        y=torch.mean(x,dim=1)\n        y=self.fc(y)\n        y=y.view(-1,1,area)\n        return x*y.expand_as(x)#将y扩展到x同样的shape","metadata":{"execution":{"iopub.status.busy":"2023-07-10T04:03:19.682395Z","iopub.execute_input":"2023-07-10T04:03:19.683066Z","iopub.status.idle":"2023-07-10T04:03:19.691401Z","shell.execute_reply.started":"2023-07-10T04:03:19.683036Z","shell.execute_reply":"2023-07-10T04:03:19.690436Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cfgs={\n    'vgg11':[64,'M',128,'M',256,256,'M',512,512,'M',512,512,'M'],\n    'vgg13':[64,64,'M',128,128,'M',256,256,'M',512,512,'M',512,512,'M'],\n    'vgg16':[64,64,'M',128,128,'M',256,256,256,'M',512,512,512,'M',512,512,512,'M'],\n    'vgg19':[64,64,'M',128,128,'M',256,256,256,256,'M',512,512,512,512,'M',512,512,512,512,'M']\n}\ndef make_feature(cfg):\n    feature_layers=[]#特征图\n    front=4#上一层的参数\n    for v in cfg:#遍历vgg的每层网络结构\n        if v==\"M\":#如果是最大池化层\n            feature_layers.append(nn.MaxPool2d(2,2))#池化核大小为2,步长为2\n        else:\n            feature_layers.append(nn.Conv2d(front,v,kernel_size=3,stride=1,padding=1))\n            feature_layers.append(nn.BatchNorm2d(v))\n            front=v\n            feature_layers.append(nn.GELU())\n    return nn.Sequential(*feature_layers)\nclass VGG(nn.Module):\n    def __init__(self):\n        #继承父类的所有方法\n        super(VGG,self).__init__()\n        \n        self.flag=0\n        self.center_array=[]\n        \n        self.conv=make_feature(cfgs['vgg19'])\n        \n        #创建自注意机制的类\n        self.Self_Attention=MultiHeadSelfAttention(dim_in=512,d_model=512,num_heads=4)\n        \n        self.seconv=SEConv()\n        \n        self.fc=nn.Sequential(\n        nn.Linear(512,2048),\n        nn.BatchNorm1d(2048),\n        nn.Dropout(0.2),\n        nn.Tanh(),\n        nn.Linear(2048,1024),\n        nn.BatchNorm1d(1024),\n        nn.GELU(),\n        nn.Linear(1024,10),\n        )   \n        \n    def circle_padding(sellf,image, pad=1):\n        \"\"\"\n        input: torch.Tensor image (batch_size, channels, height, width)\n        output: pad_image (batch_size, channels, height+2*pad, width+2*pad)\n        \"\"\"\n        b, c, h, w = image.shape\n        pad_image = torch.zeros((b, c, h+2*pad, w+2*pad), device=image.device)\n\n        pad_image[:, :, pad:h+pad, pad:w+pad] = image\n        pad_image[:, :, 0:pad, pad:w+pad] = image[:, :, -pad:, :]\n        pad_image[:, :, -pad:, pad:w+pad] = image[:, :, 0:pad, :]\n        pad_image[:, :, pad:h+pad, 0:pad] = image[:, :, :, -pad:]\n        pad_image[:, :, pad:h+pad, -pad:] = image[:, :, :, 0:pad]\n        pad_image[:, :, 0:pad, 0:pad] = image[:, :, -pad:, -pad:]\n        pad_image[:, :, -pad:, -pad:] = image[:, :, 0:pad, 0:pad]\n        pad_image[:, :, 0:pad, -pad:] = image[:, :, -pad:, 0:pad]\n        pad_image[:, :, -pad:, 0:pad] = image[:, :, 0:pad, -pad:]\n\n        return pad_image\n    def get_centering_array(self,image):\n        if self.flag==0:\n            #传入的是np.array的数组\n            batch_size,channels,row,col=image.shape\n            center_x=row/2\n            center_y=col/2\n            array=torch.randn(row,col).to(device)\n            for i in range(row):\n                for j in range(col):\n                    array[i][j]=math.sqrt(1- ( (i-center_x)**2+(j-center_y)**2)/( center_x**2+center_y**2 )/10)\n            self.flag=1\n            self.center_array=array\n        return image*self.center_array\n        \n    def forward(self,input):\n\n        output=self.circle_padding(input)\n        output=self.get_centering_array(output)\n        #卷积神经网络\n        output=self.conv(output)\n        #展平\n        output=output.view(-1,1,512)\n        output=self.seconv(output)\n        output=output+self.Self_Attention(output)#跳连接\n        output=output.view(-1,512)#reshape\n        #全连接层,原论文是4096\n        output=self.fc(output)\n        output=F.log_softmax(output/5,dim=1)\n        return output","metadata":{"execution":{"iopub.status.busy":"2023-07-10T04:03:19.692834Z","iopub.execute_input":"2023-07-10T04:03:19.693302Z","iopub.status.idle":"2023-07-10T04:03:19.719075Z","shell.execute_reply.started":"2023-07-10T04:03:19.693271Z","shell.execute_reply":"2023-07-10T04:03:19.717837Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\n\ndevice='cuda' if torch.cuda.is_available() else\"cpu\"\n\nprint(f\"device:{device}\")\n\nvalid_X1=torch.Tensor(valid_X).to(device)\nnetC=VGG()\n\ntrain_accs=[]#存储训练集的准确率\nvalid_accs=[]#存储测试集的准确率\n#训练周期为10000次\nnum_epochs=10000\nbatch_size=100#一次训练100张图片\n#优化器\noptimizer=optim.Adam(netC.parameters(),lr=0.00025,betas=(0.5,0.999))\n#损失函数\ncriterion=nn.NLLLoss()\n\nnetC=netC.to(device)\n\nfor epoch in range(num_epochs):\n    \n    random_num=[index for index in range(len(train_X))]\n    np.random.shuffle(random_num)\n    \n    train_X=train_X[random_num]\n    \n    train_y=train_y[random_num]\n    \n    train_X1=[]\n    for index in range(batch_size):\n        train_X1.append(image_Augmentation(train_X[index]))\n    train_X1=np.array(train_X1)\n    \n    train_X1=torch.Tensor(train_X1).to(device)\n    train_y1=torch.Tensor(train_y[:batch_size]).long().to(device)\n    \n    #训练\n    netC.train()\n    #将数据放进去训练\n    output=netC(train_X1).to(device)\n    #计算每次的损失函数\n    error=criterion(output,train_y1).to(device)\n    #反向传播\n    error.backward()\n    #优化器进行优化(梯度下降,降低误差)\n    optimizer.step()\n    #将梯度清空\n    optimizer.zero_grad()\n    if epoch%50==0:\n        netC.eval()\n        with torch.no_grad():\n            output=netC(train_X1).to(device)\n            output=output.detach().cpu().numpy()\n            train_pred=np.argmax(output,axis=1)\n\n            output=netC(valid_X1).to(device)\n            output=output.detach().cpu().numpy()\n            valid_pred=np.argmax(output,axis=1)\n        \n        train_acc=np.sum(train_pred==train_y[:batch_size])/len(train_y[:batch_size])\n        valid_acc=np.sum(valid_pred==valid_y)/len(valid_y)\n\n        train_accs.append(train_acc)\n        valid_accs.append(valid_acc)\n        print(f\"epoch:{epoch},error:{error}\\n train_acc:{train_acc},valid_acc:{valid_acc}\")","metadata":{"execution":{"iopub.status.busy":"2023-07-10T04:03:19.721453Z","iopub.execute_input":"2023-07-10T04:03:19.722770Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"epochs=[i*50 for i in range(len(train_accs))]\nplt.title(\"CIFAR-10\")\nplt.plot(epochs,train_accs,marker=\"o\",label=\"train_accs\")\nplt.plot(epochs,valid_accs,marker=\"x\",label=\"valid_accs\")\nplt.legend()\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#根据数据的标签和预测结果绘制混淆矩阵\ndef confusion_matrix(label,prediction):\n    \"\"\"\n    label:也就是每个数据真实的标签类别,np.array的一维数组\n    prediction:针对每个数据的预测结果,np.array的一维数组\n    \"\"\"\n    #简单做个判断,预测的个数和标签的个数要一致.\n    assert len(label)==len(prediction),\"The length of prediction and label must be the same.\"\n    num_classes=len(np.unique(label))#根据真实的标签看看有几类\n    matrix=np.zeros((num_classes,num_classes))#构建num_classes*num_classes混淆矩阵.\n    #真实标签放x轴,预测结果放y轴\n    for i in range(len(label)):\n        matrix[prediction[i]][label[i]]+=1\n    #log(x+1)\n    for i in range(num_classes):\n        for j in range(num_classes):\n            matrix[i][j]=np.log(matrix[i][j]+1)\n    #隐藏坐标轴的数字\n    #fig是画布,ax是坐标轴\n    fig,ax=plt.subplots()\n    #设置x轴和y轴的刻度\n    ax.set_xticks([])\n    ax.set_yticks([])\n    #设置颜色的调色板\n    plt.imshow(matrix,cmap='Blues')\n    #添加颜色条到图片\n    plt.colorbar()\n    #给混淆矩阵添加数字\n    for i in range(num_classes):\n        for j in range(num_classes):\n            plt.text(i, j, str(int(np.exp(matrix[j, i])-1)), ha='center', va='center', color='red')\n    #设置标签\n    plt.xlabel('True')\n    plt.ylabel('Predicted')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"netC.eval()\nwith torch.no_grad():\n    output=netC(valid_X1).to(device)\n    output=output.detach().cpu().numpy()\n    valid_pred=np.argmax(output,axis=1)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"confusion_matrix(valid_y,valid_pred)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test=pd.read_csv(\"/kaggle/input/cifar-10dataset/CIFAR-10/test.csv\")\ntest.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test=test.drop(['Unnamed: 0'],axis=1)\nX=test.values.reshape(-1,3,32,32).transpose(0,2,3,1)\nprint(f\"X.shape;{X.shape}\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_X=[]\nfor index in range(len(X)):\n    test_X.append(_generate_segments(X[index], scale=1.0, sigma=0.8, min_size=50))\ntest_X=np.array(test_X).transpose(0,3,1,2)\nprint(f\"test_X.shape:{test_X.shape}\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_label=[]\nfor i in range(len(test_X)):\n    netC.eval()\n    with torch.no_grad():\n        image=test_X[i]\n        pred=netC(torch.Tensor([image]).to(device))[0]\n        pred=np.argmax(pred.detach().cpu().numpy())\n        test_label.append(pred)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#写入文件\nimport csv\nhead=[\"index\",\"label\"]\ndata=[]\nfor index in range(len(test_label)):\n    data.append([index,test_label[index]])\nwith open(\"CIFAR-10_VGG_submission.csv\",'w',newline='') as f:\n    writer=csv.writer(f)\n    writer.writerow(head)\n    writer.writerows(data)","metadata":{},"execution_count":null,"outputs":[]}]}