{"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":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-06-13T10:02:02.528229Z","iopub.execute_input":"2023-06-13T10:02:02.528594Z","iopub.status.idle":"2023-06-13T10:02:02.539188Z","shell.execute_reply.started":"2023-06-13T10:02:02.528564Z","shell.execute_reply":"2023-06-13T10:02:02.536577Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install braincog","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:02.541759Z","iopub.execute_input":"2023-06-13T10:02:02.542508Z","iopub.status.idle":"2023-06-13T10:02:19.831231Z","shell.execute_reply.started":"2023-06-13T10:02:02.542473Z","shell.execute_reply":"2023-06-13T10:02:19.830094Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# import","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport os\nimport gc\nimport random\nfrom sklearn.model_selection import StratifiedKFold\nimport torch.backends.cudnn as cudnn\nfrom braincog.base.connection.layer import *\nfrom braincog.base.node.node import *\nfrom braincog.base.strategy.LateralInhibition import *\nfrom braincog.base.strategy.surrogate import STDPGrad\n\nfrom sklearn.metrics import confusion_matrix\nfrom dataclasses import dataclass\nfrom enum import Enum\n\nfrom tqdm import tqdm\nfrom torch import backends as backends\nfrom torch.utils.data import Dataset\nimport cloudpickle","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:19.832976Z","iopub.execute_input":"2023-06-13T10:02:19.833379Z","iopub.status.idle":"2023-06-13T10:02:24.113382Z","shell.execute_reply.started":"2023-06-13T10:02:19.833340Z","shell.execute_reply":"2023-06-13T10:02:24.112450Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# seed","metadata":{}},{"cell_type":"code","source":"seed = 42\ndef seed_everything(seed=42):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\nseed_everything(seed)","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:24.115074Z","iopub.execute_input":"2023-06-13T10:02:24.115420Z","iopub.status.idle":"2023-06-13T10:02:24.125095Z","shell.execute_reply.started":"2023-06-13T10:02:24.115387Z","shell.execute_reply":"2023-06-13T10:02:24.124206Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# set device","metadata":{}},{"cell_type":"code","source":"dev = \"cuda\"\nif torch.cuda.is_available():\n    dev = \"cuda\"\nelif backends.mps.is_available():\n    dev = \"mps\"\nelse:\n    dev = \"cpu\"\nprint(dev)\ndevice = torch.device(dev)","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:24.129057Z","iopub.execute_input":"2023-06-13T10:02:24.129503Z","iopub.status.idle":"2023-06-13T10:02:24.163592Z","shell.execute_reply.started":"2023-06-13T10:02:24.129469Z","shell.execute_reply":"2023-06-13T10:02:24.161757Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# load DS","metadata":{}},{"cell_type":"code","source":"@dataclass\nclass TrainDC:\n    Y:np.ndarray\n    X:np.ndarray","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:24.165154Z","iopub.execute_input":"2023-06-13T10:02:24.165764Z","iopub.status.idle":"2023-06-13T10:02:24.173328Z","shell.execute_reply.started":"2023-06-13T10:02:24.165728Z","shell.execute_reply":"2023-06-13T10:02:24.172496Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = pd.read_csv('/kaggle/input/digit-recognizer/train.csv')\nY = train['label'].values\nX = train.drop(labels = [\"label\"],axis = 1) / 256.\nX = X.values.reshape(-1,1,28,28)\nTrainDC.Y = Y\nTrainDC.X = X\ndel X,Y,train\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:24.174672Z","iopub.execute_input":"2023-06-13T10:02:24.175291Z","iopub.status.idle":"2023-06-13T10:02:28.176832Z","shell.execute_reply.started":"2023-06-13T10:02:24.175259Z","shell.execute_reply":"2023-06-13T10:02:28.175830Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nplt.imshow(TrainDC.X[0][0])\n","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:28.178381Z","iopub.execute_input":"2023-06-13T10:02:28.178708Z","iopub.status.idle":"2023-06-13T10:02:28.520752Z","shell.execute_reply.started":"2023-06-13T10:02:28.178676Z","shell.execute_reply":"2023-06-13T10:02:28.519877Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test = pd.read_csv('/kaggle/input/digit-recognizer/test.csv') / 256.\ntest = test.values.reshape(-1,1,28,28)","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:28.526535Z","iopub.execute_input":"2023-06-13T10:02:28.528292Z","iopub.status.idle":"2023-06-13T10:02:30.778962Z","shell.execute_reply.started":"2023-06-13T10:02:28.528239Z","shell.execute_reply":"2023-06-13T10:02:30.777982Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Modules","metadata":{}},{"cell_type":"code","source":"\nclass TensorGetterMode(Enum):\n    detach = 1\n    clone = 2\n    detach_clone = 3\n    none = 4\n\n\n# SNNレイヤーの基底クラス\nclass SNNModule(nn.Module, abc.ABC):\n    @abc.abstractmethod\n    def __init__(self, spike_getter_mode=TensorGetterMode.none) -> None:\n        self.spike_getter_mode = spike_getter_mode\n        self.__spike: None | torch.Tensor = None\n        super(SNNModule, self).__init__()\n\n    @abc.abstractmethod\n    def forward(self, x: torch.Tensor, T: int = 1) -> torch.Tensor:\n        raise NotImplementedError\n\n    @abc.abstractmethod\n    def normweight(self):\n        raise NotImplementedError\n\n    @abc.abstractmethod\n    def normgrad(self):\n        raise NotImplementedError\n\n    @abc.abstractmethod\n    def updatethresh(self):\n        raise NotImplementedError\n\n    @abc.abstractmethod\n    def reset(self):\n        raise NotImplementedError\n\n    @property\n    def spike(self) -> torch.Tensor | None:\n        # 継承先で使用、もしくはメソッドとして使用\n        # 内部で使用するときはself.__spikeを使用する\n        # self.__spikeをどの程度、グラフから切り離すかは、self.spike_getter_modeで決定する\n        # ただ単に＿＿spikeの値を確認したいだけの場合は、detach_clone modeを推奨\n        if self.__spike is None:\n            return None\n        if self.spike_getter_mode == TensorGetterMode.detach:\n            return self.__spike.detach()\n        elif self.spike_getter_mode == TensorGetterMode.clone:\n            return self.__spike.clone()\n        elif self.spike_getter_mode == TensorGetterMode.detach_clone:\n            return self.__spike.detach().clone()\n        else:\n            return self.__spike\n        \n    @spike.setter\n    def spike(self, value: torch.Tensor | None) -> None:\n        self.__spike = value\n\n\nclass ASFModule(nn.Module):\n    def __init__(self) -> None:\n        super(ASFModule, self).__init__()\n\n    def forward(self, x: torch.Tensor, threshold: torch.Tensor) -> torch.Tensor:\n        x = x.clamp(min=0)  # 負の値を0にする\n        x = threshold / (\n            1 + torch.exp(-(x - 4 * threshold / 10) * (8 / threshold))\n        )  # ASF\n        # tにおけるthresholdを1+exp δtで割る\n        # δtの実装が論文と違う\n        return x\n    \nclass Normliaze(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n    def forward(self, x):  # [batch,T,c,w,h]\n        x /= x.max(1, True)[0].max(2, True)[0].max(3, True)[0]\n\n        return x\n\n\n# ニューラルエンコーディング\nclass SNNEncoding(SNNModule):\n    def __init__(self, tau_decay, inh=6, ASF: nn.Module | None = ASFModule()) -> None:\n        super(SNNEncoding, self).__init__()\n        self.node = LIFSTDPNode(act_fun=STDPGrad, tau=tau_decay, mem_detach=True)\n        self.WTA = WTALayer()#発火したニューロンからランダムに一つ選ぶ\n        self.lateralinh = LateralInhibition(inh=inh, node=self.node,mode=\"threshold\")\n        self.ASF = ASF\n        self.__spike: torch.Tensor | None = None\n\n    def get_threshold(self, x: torch.Tensor) -> torch.Tensor:\n        tmp2 = x.max(0, True)[0].max(2, True)[0].max(3, True)[0] + 0.0001\n        return tmp2\n\n    def forward(self, x: torch.Tensor, T: int = 1) -> torch.Tensor:\n        n = self.get_threshold(x)  # 発火しないギリギリの閾値\n        self.node.threshold.data = n\n        if self.ASF is not None:\n            x = self.ASF(x, n)\n        xsum: float | torch.Tensor = 0.0\n        for t in range(T):\n            y = self.node(x)\n            if y.max() > 0:\n                y = self.WTA(y)\n                self.lateralinh(y)\n            self.__spike = y\n            xsum += self.__spike\n        # 型エラーにならないように\n        if isinstance(xsum, float):\n            raise TypeError\n        return xsum\n\n    def normweight(self) -> None:\n        pass\n\n    def normgrad(self) -> None:\n        pass\n\n    def updatethresh(self) -> None:\n        pass\n\n    def reset(self) -> None:\n        self.node.n_reset()\n\n\nclass SNNConv2DEncoding(SNNEncoding):\n    # conv2dの引数を受け取る\n    def __init__(\n        self,\n        in_channels: int,\n        out_channels: int,\n        kernel_size: int,\n        stride: int = 1,\n        padding: int = 0,\n        dilation: int = 1,\n        groups: int = 1,\n        bias: bool = False,\n        padding_mode: str = \"zeros\",\n        device=None,\n        dtype=None,\n        tau_decay: float = np.exp(-1 / 100),\n        inh: float = 6,\n        ASF: nn.Module = ASFModule(),\n        offset: float = 0.3,\n    ):\n        super(SNNConv2DEncoding, self).__init__(tau_decay, inh, ASF).__init__()\n        self.conv = nn.Conv2d(\n            in_channels=in_channels,\n            out_channels=out_channels,\n            kernel_size=kernel_size,\n            stride=stride,\n            padding=padding,\n            dilation=dilation,\n            groups=groups,\n            bias=bias,\n            padding_mode=padding_mode,\n            device=device,\n            dtype=dtype,\n        )\n        self.conv.weight.requires_grad = False\n        self.offset = offset\n        self.normweight()\n\n    def forward(self, x: torch.Tensor, T: int = 1) -> torch.Tensor:\n        x = self.conv(x)\n        return super().forward(x, T)  # Conv2dの出力をSNNEncodingに入力\n\n    def normgrad(self):\n        #使わない\n        if self.conv.weight.grad is None:\n            return\n        if self.spike is None:\n            return\n        tmp = self.offset * self.spike.mean(0, True).mean(2, True).mean(\n            3, True\n        ).permute(1, 0, 2, 3)\n        self.conv.weight.grad.data -= tmp\n        self.conv.weight.grad.data = -self.conv.weight.grad.data\n\n    def normweight(self):\n        # 標準化\n        # 平均を0にする\n        # 標準偏差を1にする\n        # 最初だけ呼ばれる\n        c, i, w, h = self.conv.weight.data.shape\n        avg = self.conv.weight.data.mean(1, True).mean(2, True).mean(3, True)\n        self.conv.weight.data -= avg\n\n        tmp = self.conv.weight.data.reshape(c, 1, -1, 1)\n\n        self.conv.weight.data /= tmp.std(2, unbiased=False, keepdim=True)\n\n    def updatethresh(self):\n        pass\n\n    def reset(self):\n        super().reset()  # self.node.n_reset()\n\n\n# 学習を行う\nclass STDP_LIF_Layer(SNNModule):\n    def __init__(\n        self, tau_decay=0.99, inh=10.0, in_features=-1, out_features=10\n    ) -> None:\n        super().__init__(spike_getter_mode=TensorGetterMode.none)\n        self.node = LIFSTDPNode(\n            act_fun=STDPGrad, tau=tau_decay, mem_detach=True\n        )  # 発火モデルの定義と学習規則の定義\n        self.WTA = WTALayer()\n        self.lateralinh = LateralInhibition(inh=inh, node=self.node,mode=\"max\") \n        self.weight = nn.LazyLinear(\n            out_features, bias=False\n        )  # 全結合レイヤーを重みとして使用　バックプロパケーションではなくNodeの学習規則を使用する\n        self.alpha = 0.002  # ATBで使う係数Alphaの実装\n        self.gamma = 350  # ATBで使う閾値Gammaの実装\n        self.in_features = in_features\n        self.x: torch.Tensor\n        self.init=False\n\n    def forward(self, x: torch.Tensor, T: int = 1) -> torch.Tensor:\n        x = x.detach()\n        xsum: float | torch.Tensor = 0.0\n        \n        x = self.weight(x)  # 重みをかける\n        self.x = x.detach()\n        if not self.init:\n            self.node.threshold.data = (x.max(0)[0].detach()*3).to(device)\n            self.init=True\n        for t in range(T):\n            xori=x\n            y = self.node(x)  # 発火\n            if y.max()>0:\n                y = self.WTA(y)\n                y = self.lateralinh(y, xori)#侧抑制\n            self.spike = y\n            xsum += y\n        if isinstance(xsum, float):\n            raise TypeError\n        return xsum\n\n    def reset(self):\n        return self.node.n_reset()\n\n    def normweight(self):\n        self.weight.weight.data = torch.clamp(\n            self.weight.weight.data, min=0, max=1.0\n        )  # 0~1にclamp\n        self.weight.weight.data /= self.weight.weight.data.max(1, True)[0] / 0.1\n        # 重みの最大値を0.1にする\n\n    def normgrad(self):\n        self.weight.weight.grad.data = -self.weight.weight.grad.data  # type:ignore\n\n    def updatethresh(self):  # ATB\n        if self.spike is None:\n            return\n        self.node.threshold.data += (self.alpha*self.x * self.spike.detach()).sum(0)#発火したニューロンの入力を閾値に足す\n        theta_bias = self.node.threshold.max() - self.gamma#閾値の最大値とgammaの差\n        if theta_bias > 0:#閾値がgammaを超えたら\n            self.node.threshold.data -= theta_bias\n","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:30.781093Z","iopub.execute_input":"2023-06-13T10:02:30.781673Z","iopub.status.idle":"2023-06-13T10:02:30.824099Z","shell.execute_reply.started":"2023-06-13T10:02:30.781639Z","shell.execute_reply":"2023-06-13T10:02:30.823019Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Voting(nn.Module):\n\n    def __init__(self, shape):\n        super().__init__()\n        self.label = torch.zeros(shape) - 1\n        self.assignments:torch.Tensor\n        self.alpha = 0.7\n    def assign_labels(self, spikes:torch.Tensor, labels, rates=None, n_labels=10):\n        # labels 正解ラベル\n        # spikes => batch * time * in_size\n        # print(spikes.size())\n        n_neurons = spikes.size(2)\n        if rates is None:\n            rates = torch.zeros(n_neurons, n_labels, device=device)#node数*label数\n        self.n_labels = n_labels\n        #時間方向に合計\n        spikes = spikes.cpu().sum(1).to(device)#batch*time*in_size->batch*in_size\n\n        for i in range(n_labels):\n            n_labeled = torch.sum(labels == i).float()  # label iの数\n            if n_labeled > 0:\n                indices = torch.nonzero(labels == i).view(-1) # label iのインデックス\n                tmp = torch.sum(spikes[indices], 0) / n_labeled  # label iのときのバッチのSpikeの合計をlabel iの回数で割る\n                rates[:, i] = self.alpha * rates[:, i] + tmp #過去からの引き継ぎ＊Alpha+今回の値\n\n        self.assignments = torch.max(rates, 1)[1]#もっとも対応したラベルをニューロンに割り当てる\n        return self.assignments, rates\n    def get_label(self, spikes:torch.Tensor):\n        n_samples = spikes.size(0)#batch\n        #時間方向に合計\n        spikes = spikes.cpu().sum(1).to(device)#batch*time*in_size->batch*in_size\n\n        rates = torch.zeros(n_samples, self.n_labels, device=device)#batch*label数\n\n        for i in range(self.n_labels):\n            n_assigns = torch.sum(self.assignments == i).float()  #そのクラスに対応したニューロンの数\n            if n_assigns > 0:\n                indices = torch.nonzero(self.assignments == i).view(-1)  # そのクラスに対応したニューロンのインデックス\n                rates[:, i] = torch.sum(spikes[:, indices], 1) / n_assigns  # そのクラスに対応したニューロンのスパイクの合計をそのクラスに対応したニューロンの数で割る\n\n        return torch.sort(rates, dim=1, descending=True)[1][:, 0]#もっともスパイクが多いクラスを返す","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:30.826028Z","iopub.execute_input":"2023-06-13T10:02:30.826498Z","iopub.status.idle":"2023-06-13T10:02:30.843842Z","shell.execute_reply.started":"2023-06-13T10:02:30.826464Z","shell.execute_reply":"2023-06-13T10:02:30.843019Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Debug_Layer(nn.Module):\n    def forward(self, x: torch.Tensor) -> torch.Tensor:\n\n        return x\nclass SNN_Conv_Model(SNNModule):\n    def __init__(self, spike_getter_mode=TensorGetterMode.none) -> None:\n        super(SNN_Conv_Model,self).__init__()\n        self.layers = nn.ModuleList(\n            [\n                SNNConv2DEncoding(\n                    in_channels=1,\n                    out_channels=12,\n                    kernel_size=3,\n                    stride=1,\n                    padding=1,\n                    inh=1.625,\n                ),\n                nn.MaxPool2d(kernel_size=2, stride=2,padding=0),\n                Normliaze(),\n                nn.Flatten(start_dim=1),\n                STDP_LIF_Layer(\n                    out_features=256,\n                    inh=25,\n                ),\n            ]\n        )\n        self.prediction = Voting(10)\n    def forward(self, x: torch.Tensor, T: int = 1) -> torch.Tensor:\n        for module in self.layers:\n            if isinstance(module, SNNModule):\n                x = module(x, T)\n            else:\n                x = module(x)\n        return x\n    def reset(self):\n        for module in self.layers:\n            if isinstance(module, SNNModule):\n                module.reset()\n    def normweight(self):\n        for module in self.layers:\n            if isinstance(module, STDP_LIF_Layer):\n                module.normweight()\n    def normgrad(self):\n        for module in self.layers:\n            if isinstance(module, STDP_LIF_Layer):\n                module.normgrad()\n    def updatethresh(self):\n        for module in self.layers:\n            if isinstance(module, STDP_LIF_Layer):\n                module.updatethresh()","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:30.845831Z","iopub.execute_input":"2023-06-13T10:02:30.846243Z","iopub.status.idle":"2023-06-13T10:02:30.859521Z","shell.execute_reply.started":"2023-06-13T10:02:30.846209Z","shell.execute_reply":"2023-06-13T10:02:30.858603Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Early Stopping","metadata":{}},{"cell_type":"code","source":"class EarlyStopping:\n    def __init__(self,patience=8,max=False , verbose=False, path='checkpoint_model.pkl')->None:\n        self.patience = patience\n        self.max = max\n        self.verbose = verbose\n        self.path = path\n        self.cnt:int = 0\n        self.best:None|float = None\n    def __call__(self,val_loss,model)->bool:\n        if self.max:\n            score = -val_loss\n        else:\n            score = val_loss\n        if self.best is None:\n            self.best = score\n        elif self.best > score:\n            self.best = score\n        elif self.best == score:\n            self.cnt = 0\n        else:\n            self.cnt += 1\n            if self.cnt > self.patience:\n                return True\n            return False\n        if self.verbose:\n            print(f\"best is {val_loss}\")\n        self.checkpoint(model)\n        self.cnt = 0\n        return False\n    def checkpoint(self, model):\n        with open(self.path,'wb') as f:\n            cloudpickle.dump(model, f)\n        \n    def reset(self,path):\n        self.path = path\n        self.best = None\n        \n        \n        \n        ","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:30.860848Z","iopub.execute_input":"2023-06-13T10:02:30.861415Z","iopub.status.idle":"2023-06-13T10:02:30.874990Z","shell.execute_reply.started":"2023-06-13T10:02:30.861383Z","shell.execute_reply":"2023-06-13T10:02:30.873986Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# summary","metadata":{}},{"cell_type":"code","source":"from torchinfo import summary\nsummary(SNN_Conv_Model(), (1,1, 28, 28))","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:30.879623Z","iopub.execute_input":"2023-06-13T10:02:30.879966Z","iopub.status.idle":"2023-06-13T10:02:38.402849Z","shell.execute_reply.started":"2023-06-13T10:02:30.879918Z","shell.execute_reply":"2023-06-13T10:02:38.401854Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# loop with voting layer","metadata":{}},{"cell_type":"code","source":"def train_loop(train_iter, test_iter, model:SNN_Conv_Model, es:EarlyStopping, optimizer:torch.optim.Optimizer,T:int=64,epoc:int=1000)->float:\n    rates:None|torch.Tensor=None\n    for epoch in range(epoc):\n        spikefull:None|torch.Tensor  = None\n        labelfull:None|torch.Tensor = None\n        for step, (x, y) in enumerate(tqdm(train_iter)):\n            x = x.to(device)\n            y = y.to(device)\n            \n            spikes=model(x,T)\n\n            optimizer.zero_grad()\n            spikes.sum().backward()\n            for i in range(len(model.layers)):\n                if isinstance(model.layers[i], STDP_LIF_Layer):\n                    model.layers[i].spike=spikes.detach()\n            model.normgrad()\n            optimizer.step()\n            model.updatethresh()\n            model.normweight()\n\n            spikes=spikes.reshape(spikes.shape[0],1,-1).detach()\n            if spikefull is None:\n                spikefull=spikes\n                labelfull=y\n            elif labelfull is not None:\n                spikefull=torch.cat([spikefull,spikes],0)\n                labelfull=torch.cat([labelfull,y],0)\n            model.reset()\n        if spikefull is None or labelfull is None:\n            raise Exception(\"no data\")\n        _,rates=model.prediction.assign_labels(spikefull,labelfull,rates=rates)\n        rates=rates.detach()\n        result= model.prediction.get_label(spikefull)\n        acc=(result==labelfull).float().mean()\n        model.eval()\n        with torch.no_grad():\n            vaild = vaild_loop(model,test_iter,T)\n        model.train()\n        print(f\"epoch:{epoch} acc:{acc} vaild:{vaild}\")\n        if es(vaild,model):\n            print(\"Stopping!!!\")\n            break\n        #break\n    del spikefull,labelfull\n    gc.collect()\n    return es.best","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:38.404280Z","iopub.execute_input":"2023-06-13T10:02:38.404636Z","iopub.status.idle":"2023-06-13T10:02:38.417473Z","shell.execute_reply.started":"2023-06-13T10:02:38.404603Z","shell.execute_reply":"2023-06-13T10:02:38.416479Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def vaild_loop(model,test_iter,T):\n    spikefull = None\n    labelfull = None\n    for step2, (x, y) in enumerate(test_iter):\n        x = x.to(device)\n        y = y.to(device)\n\n        spikes = model(x,T)\n\n        spikes = spikes.reshape(spikes.shape[0], 1, -1).detach()\n\n        with torch.no_grad():\n            if spikefull is None:\n                spikefull = spikes\n                labelfull = y\n\n            else:\n                spikefull = torch.cat([spikefull, spikes], 0)\n                labelfull = torch.cat([labelfull, y], 0)\n\n        model.reset()\n    result = model.prediction.get_label(spikefull)\n    acc = (result == labelfull).float().mean()\n    return acc","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:38.419206Z","iopub.execute_input":"2023-06-13T10:02:38.419586Z","iopub.status.idle":"2023-06-13T10:02:38.431012Z","shell.execute_reply.started":"2023-06-13T10:02:38.419553Z","shell.execute_reply":"2023-06-13T10:02:38.430177Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# train setting","metadata":{}},{"cell_type":"code","source":"@dataclass\nclass SNNTrainSettings:\n    batch_size = 4096\n    epoch = 32\n    T = 100\n    lr = 0.0001\n    es = EarlyStopping(max=True,patience=4)\n    n_fold = 5","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:38.432226Z","iopub.execute_input":"2023-06-13T10:02:38.432638Z","iopub.status.idle":"2023-06-13T10:02:38.444682Z","shell.execute_reply.started":"2023-06-13T10:02:38.432606Z","shell.execute_reply":"2023-06-13T10:02:38.443660Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Fold","metadata":{}},{"cell_type":"code","source":"skf = StratifiedKFold(n_splits=SNNTrainSettings.n_fold,random_state=seed,shuffle = True)\nfolds = []\nscores = []\nfor i, (train_index, test_index) in enumerate(skf.split(TrainDC.X, TrainDC.Y)):\n    train_X = TrainDC.X[train_index]\n    train_Y = TrainDC.Y[train_index]\n    \n    train_X = torch.tensor(train_X, dtype=torch.float32)\n    train_Y = torch.tensor(train_Y, dtype=torch.int64)\n\n    train_DS = torch.utils.data.TensorDataset(train_X, train_Y)\n    \n    train_iter = torch.utils.data.DataLoader(train_DS, batch_size=SNNTrainSettings.batch_size, shuffle=True, num_workers=4)\n    \n    \n    test_X = TrainDC.X[test_index]\n    test_Y = TrainDC.Y[test_index]\n    \n    test_X = torch.tensor(test_X, dtype=torch.float32)\n    test_Y = torch.tensor(test_Y, dtype=torch.int64)\n    \n    test_DS = torch.utils.data.TensorDataset(test_X, test_Y)\n    \n    test_iter = torch.utils.data.DataLoader(test_DS, batch_size=SNNTrainSettings.batch_size, shuffle=True, num_workers=4)\n    \n    \n    model = SNN_Conv_Model().to(device)\n    params = list(model.parameters())\n    \n    optimizer = torch.optim.SGD(params, lr=SNNTrainSettings.lr)\n    SNNTrainSettings.es.reset(f\"check_{i}.pkl\")\n    \n    score = train_loop(train_iter,test_iter, model, SNNTrainSettings.es , optimizer, SNNTrainSettings.T,SNNTrainSettings.epoch)\n    scores.append(score.to('cpu'))\n    del train_X,train_Y,train_DS,train_iter,test_X,test_Y,test_DS,test_iter\n    gc.collect()\n\nprint(f'this is CV:{np.mean(scores)*-1}')\n\n    ","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:02:38.446200Z","iopub.execute_input":"2023-06-13T10:02:38.446720Z","iopub.status.idle":"2023-06-13T10:03:49.302753Z","shell.execute_reply.started":"2023-06-13T10:02:38.446686Z","shell.execute_reply":"2023-06-13T10:03:49.301545Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"results = []\nfor i in range(SNNTrainSettings.n_fold):\n    model = None\n    with open(f'check_{i}.pkl', 'rb') as f:\n        model = cloudpickle.load(f)\n    if model is None:\n        print(\"Error\")\n        break\n    model.eval()\n    test_X = torch.tensor(test, dtype=torch.float32).to(device)\n    with torch.no_grad():\n        spikes = model(test_X,SNNTrainSettings.T)\n        spikes = spikes.reshape(spikes.shape[0], 1, -1)\n        model.reset()\n        result = model.prediction.get_label(spikes)\n    results.append(result)","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:03:49.304590Z","iopub.execute_input":"2023-06-13T10:03:49.304982Z","iopub.status.idle":"2023-06-13T10:04:29.121582Z","shell.execute_reply.started":"2023-06-13T10:03:49.304947Z","shell.execute_reply":"2023-06-13T10:04:29.120540Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for i in range(len(results)):\n    results[i]=results[i].to('cpu').numpy()\nresult = np.apply_along_axis(lambda x: np.bincount(x).argmax(), axis=0, arr=results)\n\nprint(result)","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:04:29.123080Z","iopub.execute_input":"2023-06-13T10:04:29.123523Z","iopub.status.idle":"2023-06-13T10:04:29.292989Z","shell.execute_reply.started":"2023-06-13T10:04:29.123490Z","shell.execute_reply":"2023-06-13T10:04:29.292014Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"output = pd.DataFrame({'Label': result}).reset_index(drop=True)\noutput.index += 1\noutput.to_csv('sub.csv', index_label='ImageId')","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:04:29.294269Z","iopub.execute_input":"2023-06-13T10:04:29.295159Z","iopub.status.idle":"2023-06-13T10:04:29.367600Z","shell.execute_reply.started":"2023-06-13T10:04:29.295124Z","shell.execute_reply":"2023-06-13T10:04:29.365823Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del model,test_X,spikes\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2023-06-13T10:04:29.974378Z","iopub.status.idle":"2023-06-13T10:04:29.975086Z","shell.execute_reply.started":"2023-06-13T10:04:29.974827Z","shell.execute_reply":"2023-06-13T10:04:29.974850Z"},"trusted":true},"execution_count":null,"outputs":[]}]}