{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom pathlib import Path\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\ntry:\n    if torch.cuda.is_available():\n        import torch.cuda.amp\n        MIXED_PRECISION = True\n    else:\n        MIXED_PRECISION = False  # not installed\nexcept:\n    print('Pytorch>=1.6 is recommended for faster mixed precision training')\n    MIXED_PRECISION = False  # not installed\nclass build_model():\n    def __init__(self, network):\n        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n        #self.network = PLSnet(2, 1)\n        self.network = network\n        #self.init_weights(self.network)\n        self.optimizer = torch.optim.Adam(self.network.parameters(), lr=1, betas=[0.9, 0.999])\n        #self.optimizer = pytorch_lamb.Lamb(self.network.parameters(), lr=0.002, betas=(.9, .999), adam=True)\n        #self.optimizer = pytorch_lamb.LARS(self.network.parameters(), lr=0.1, momentum=0.9, weight_decay=5e-4, eta=0.001, max_epoch=200)\n        self.network.to(self.device)\n        if MIXED_PRECISION:\n            self.scaler = torch.cuda.amp.GradScaler()\n    def __call__(self,*inputs):\n        if self.network.training:\n            return self.network(*inputs)\n        else:\n            with torch.no_grad():\n                return self.network(*inputs)\n    def train(self):\n        self.network.train()\n    def eval(self):\n        self.network.eval()\n    def set_lr(self, new_lr):\n        for g in self.optimizer.param_groups:\n            g['lr'] = new_lr\n    def init_weights(self, m):\n        for child in m.children():\n            if isinstance(child, torch.nn.Module):\n                if hasattr(child, 'weight'):\n                    torch.nn.init.normal_(child.weight, 0.0, 0.01)\n                    #torch.nn.init.xavier_normal_(child.weight) # sqrt(1/前の層のノード数)　を標準偏差とする。Xivier初期化\n                    #torch.nn.init.kaiming_normal_(child.weight) # sqrt(2/前の層のノード数)　を標準偏差とする。He初期化\n                if hasattr(child, 'bias'):\n                    if child.bias is not None: torch.nn.init.normal_(child.bias, 0.0, 0.01)\n                self.init_weights(child)\n    def update(self, loss, do_step=True): # 勾配を貯めたい場合は do_step = False　にする。\n        if self.network.training:\n            self.optimizer.zero_grad()\n            if MIXED_PRECISION:\n                self.scaler.scale(loss).backward()\n                self.scaler.step(self.optimizer)\n                if do_step: self.scaler.update()\n            else:\n                loss.backward()\n                if do_step: self.optimizer.step()\n    def save(self, fn=None):\n        _fn = 'network_params.pth' if fn is None else fn\n        torch.save(self.network.state_dict(), _fn)\n    def load(self, fn=None):\n        _fn = 'network_params.pth' if fn is None else fn\n        self.network.load_state_dict(torch.load(_fn))\n    def gradient_penalty(self, loss): # 勾配の二乗をロスに加えるサンプルコード。 torch.cuda.ampが使えるときに使用可能\n        grad_params = torch.autograd.grad(loss, self.network.parameters(), create_graph=True)\n        grad_norm = 0\n        for grad in grad_params:\n            grad_norm += grad.pow(2).sum()\n        grad_norm = grad_norm.sqrt()\n        loss = loss + grad_norm\n\nclass curve(nn.Module):\n    def __init__(self):\n        super(curve, self).__init__()\n        self.fvc = nn.Parameter(torch.Tensor(np.zeros(150)))\n    def set_init(self, fvc_true, weeks):\n        xp = weeks.cpu().numpy()+12\n        fp = fvc_true.cpu().numpy()\n        fp_start = fp[0] - (fp[0]-fp[-1])/(xp[0]-xp[-1])*(xp[0] - 0)**0.5\n        fp_end = fp[-1] + (fp[0]-fp[-1])/(xp[0]-xp[-1])*(150 - xp[-1])**0.5\n        fp = np.concatenate([[fp_start], fp, [fp_end]])\n        xp = np.concatenate([[0], xp, [149]])\n        init = np.interp(np.arange(150), xp, fp)\n        self.fvc.data = torch.Tensor(init)\n    def calc_loss(self, fvc_true, weeks):\n        inner_idx = weeks.long()+12\n        fvc_pred = self.fvc[inner_idx]\n        l1_loss = (fvc_true - fvc_pred).abs()\n        d1 = self.fvc[:-1] - self.fvc[1:]\n        d2 = d1[:-1] - d1[1:]\n        d2_5 = d1[:-5] - d1[5:]\n        d2_10 = d1[:-10] - d1[10:]\n        d2_20 = d1[:-20] - d1[20:]\n        d1 = torch.cat([d1, d1[-1:]])\n        d2 = torch.cat([d2[:1], d2, d2[-1:]])\n        d2_5 = torch.cat([d2_5[:3], d2_5, d2_5[-3:]])\n        d2_10 = torch.cat([d2_10[:5], d2_10, d2_10[-6:]])\n        d2_20 = torch.cat([d2_20[:10], d2_20, d2_20[-11:]])\n        d0_loss = self.fvc.square()\n        d1_loss = d1.square()\n        d2_loss = d2.square()\n        d2_5_loss = d2_5.square()\n        d2_10_loss = d2_10.square()\n        d2_20_loss = d2_20.square()\n        loss_reg = torch.stack([d1_loss, d2_loss, d2_5_loss, d2_10_loss, d2_20_loss], 1)\n        inner_loss = l1_loss.mean() + torch.Tensor([1,1,1,1,0]) @ loss_reg[inner_idx.min():inner_idx.max()-9].mean(0)\n        outer1_loss = torch.Tensor([1,1,1,1,0]) @ loss_reg[:inner_idx.min()+10].mean(0)\n        outer2_loss = torch.Tensor([0,10,5,1,1]) @ loss_reg[inner_idx.max()-9:].mean(0)\n        return outer1_loss + inner_loss + outer2_loss","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\"\"\" data load\"\"\"\nroot_dir = Path('../input/osic-pulmonary-fibrosis-progression')\n\nds = pd.read_csv(Path(root_dir)/\"train.csv\")\nds.drop_duplicates(keep=False, inplace=True, subset=['Patient', 'Weeks'])\nds.set_index('Patient')\nps = ds.Patient.unique()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\"\"\" Sample curve fitting \"\"\"\nFVC_pred = np.zeros((len(ps),150))\nfor i in range(len(ps)):\n    #i = 23\n    patient = ps[i]\n    p = ds[ds.Patient==patient].sort_values(by='Weeks')\n    model = build_model(curve())\n    self = model.network\n    fvc_true = torch.Tensor(p.FVC.values).to(model.device)\n    weeks = torch.Tensor(p.Weeks.values).to(model.device)\n    model.network.set_init(fvc_true, weeks)\n    model.set_lr(1)\n    for _ in range(300):\n        loss = model.network.calc_loss(fvc_true, weeks)\n        #print(loss)\n        model.update(loss)\n    smooth_fvc = model.network.fvc.detach().cpu().numpy()\n    FVC_pred[i] = smooth_fvc\n\n    print('\\r{}'.format(i),end='')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def seek_sigma(FVC_pred, FVC_true):\n    delta = np.abs(FVC_pred - FVC_true)[None]\n    sigma = (delta.mean() + np.arange(300)/10 - 3)[:,None]\n    score = -np.sqrt(2) * delta / sigma - np.log(np.sqrt(2) * sigma)\n    score = score.mean(1)\n    return sigma[:,0][score==score.min()][0]\ndef seek_sigma_by_delta(delta):\n    delta = np.array(delta)[None]\n    sigma = (delta.mean() + np.arange(300)/10 - 3)[:,None]\n    score = -np.sqrt(2) * delta / sigma - np.log(np.sqrt(2) * sigma)\n    score = score.mean(1)\n    return sigma[:,0][score==score.min()][0], score.min()\nsigma = np.zeros(len(ps))\nscore = np.zeros(len(ps))\nfor i in range(len(ps)):\n    #sigma[i],score[i] = seek_sigma_by_delta(deltas[i])\n    patient = ps[i]\n    p = ds[ds.Patient==patient].sort_values(by='Weeks')\n    fvc_true = p.FVC.values\n    fvc_pred = FVC_pred[i][p.Weeks.values]\n    sigma[i] = seek_sigma(fvc_pred, fvc_true)\n    print('\\r{}'.format(i+1),end='')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = pd.read_csv('../input/osic-pulmonary-fibrosis-progression/sample_submission.csv')\nsubmission['Patient'] = submission['Patient_Week'].apply(lambda x: x.split('_')[0])\nsubmission['predict_Week'] = submission['Patient_Week'].apply(lambda x: x.split('_')[1]).astype(int)\n\ntest = pd.read_csv('../input/osic-pulmonary-fibrosis-progression/test.csv')\\\n        .rename(columns={'Weeks': 'base_Week', 'FVC': 'base_FVC', 'Percent': 'base_Percent', 'Age': 'base_Age'})\ntest = submission.drop(columns=['FVC', 'Confidence']).merge(test, on='Patient')\ntest['Week_passed'] = test['predict_Week'] - test['base_Week']\n\nsubmission = submission.drop(columns=['Patient', 'predict_Week'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"FVC_sub = []\nsigma_sub = []\nfor r, rowitem in test.iterrows():\n    patient = rowitem.Patient\n    w = rowitem.predict_Week\n    FVC_sub.append( FVC_pred[ps==patient][0,w+12] )\n    sigma_sub.append( sigma[ps==patient][0] )\ntest['FVC_pred'] = FVC_sub\ntest['Confidence'] = 400","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\nsub = submission.drop(columns=['FVC', 'Confidence']).merge(test[['Patient_Week', 'FVC_pred', 'Confidence']], \n                                                           on='Patient_Week')\nsub.columns = submission.columns\nsub.to_csv('submission.csv', index=False)\nsub.head()","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}