{"cells":[{"metadata":{},"cell_type":"markdown","source":"In this kernel, we train a custom GCN (based on the starter kit provided by @hengck23 ) on 2JHN. For a description of our final models, you can read the following discussions: \nhttps://www.kaggle.com/c/champs-scalar-coupling/discussion/106271#latest-610727 \nhttps://www.kaggle.com/c/champs-scalar-coupling/discussion/106293#latest-611400\n\nAs well as using the node and edge features for each molecule, we found success including some of the features used in our earlier LightGBM models. The LGB features that we included depended on the #J, because features like dihedral angles aren't so useful for 1J."},{"metadata":{"trusted":true},"cell_type":"code","source":"TYPE_TO_TRAIN = '2JHN'\nNUM_ITERS = 20 * 1000 # change this to a high number, i.e., 100000\nBATCH_SIZE = 70 #batch size over 100 usually leads to memory errors.\nNUM_PROPAGATE = 2 #increasing num propagate leads to longer training times.  Make sure you adjust Num_iters correctly so the kernel doesn't time out\nNUM_S2S=6\n\nKFOLD_SEED = 2020\nN_FOLD = 8\nFOLD_TO_TRAIN = 0 # fold index to train, only specify from 0 to N_FOLD-1\n\nDATA_DIR = '../input/champs-scalar-coupling/'\nSPLIT_DIR = '../input/champs-scalar-coupling/'\nGRAPH_DIR = '../input/gcn-data-full-v2/'\nGRAPH_TRAIN_PICKLE = 'graph-train-full.pickle'\nGRAPH_TEST_PICKLE = 'graph-test.pickle'\n\n#controls which secondLayerFeat are used - uncomment the one associated with your type\n#LGB_COLS = list(range(41))+[44,45,46,47,48,49]          #1J\nLGB_COLS = list(range(42))+[44,45,46,47,50,51,52]       #2J\n#LGB_COLS = list(range(44))+[44,45,46,47,53,54,55,56]     #3J\nNODE_COLS = range(61) #controls which node features are used\n\nRANDOM_SEED = 1991\nSTARTING_LR=.001","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"NUM_TARGET =  8\n\nimport numpy as np\nimport pandas as pd\nfrom sklearn.model_selection import KFold\n!conda install pytorch torchvision -c pytorch --yes\nimport torchvision\nimport torch\n\ntorch.cuda.manual_seed(1)\ntorch.manual_seed(12345)\ntorch.backends.cudnn.deterministic = True\ntorch.backends.cudnn.benchmark = False\nnp.random.seed(0)\n!pip install torch-scatter\n!pip install torch_geometric\n!pip install torch_sparse\n!pip install torch_cluster\n\nimport copy, math, numpy as np, random, PIL\n\n# torch libs\nimport torch\nfrom torch.utils.data.dataset import Dataset\nfrom torch.utils.data import DataLoader\nfrom torch.utils.data.sampler import *\n\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom torch.nn.parallel.data_parallel import data_parallel\nfrom torch.nn.utils.rnn import *\n\n# std libs\nfrom timeit import default_timer as timer\nfrom collections import OrderedDict\nfrom multiprocessing import Pool\nimport multiprocessing as mp\n\n#from pprintpp import pprint, pformat\nimport json, zipfile, csv, pandas as pd, pickle, glob, sys, time, copy, numbers, inspect, shutil, itertools \nfrom distutils.dir_util import copy_tree\nimport matplotlib.pyplot as plt\nfrom mpl_toolkits.mplot3d import Axes3D\n\nimport os\nos.environ['CUDA_VISIBLE_DEVICES']='0'\nfrom torch_scatter import *\nfrom torch_geometric.utils import scatter_\nimport torch.nn as nn\nfrom torch_geometric.nn import GATConv\n# constant #\nPI, INF, EPS = np.pi, np.inf, 1e-12","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def random_seed(seed_value, use_cuda):\n    np.random.seed(seed_value) # cpu vars\n    torch.manual_seed(seed_value) # cpu  vars\n    random.seed(seed_value) # Python\n    if use_cuda: \n        torch.cuda.manual_seed(seed_value)\n        torch.cuda.manual_seed_all(seed_value) # gpu vars\n        torch.backends.cudnn.deterministic = True  #needed\n        torch.backends.cudnn.benchmark = False\nrandom_seed(RANDOM_SEED,True)       \nclass DecayScheduler():\n    def __init__(self, base_lr, decay, step):\n        super(DecayScheduler, self).__init__()\n        self.step  = step\n        self.decay = decay\n        self.base_lr = base_lr\n    def get_rate(self, epoch):\n        lr = self.base_lr * (self.decay**(epoch // self.step))\n        return lr\n\nclass NullScheduler():\n    def __init__(self, lr=0.01 ):\n        super(NullScheduler, self).__init__()\n        self.lr    = lr\n        self.cycle = 0\n    def get_rate(self, epoch):\n        return self.lr \n\n    \n    \ndef adjust_learning_rate(optimizer, lr):\n    for param_group in optimizer.param_groups:\n        param_group['lr'] = lr\n\ndef get_learning_rate(optimizer):\n    lr=[]\n    for param_group in optimizer.param_groups: lr +=[ param_group['lr'] ]\n    assert(len(lr)==1) #we support only one param_group\n    lr = lr[0]\n    return lr\n\ndef time_to_str(t, mode='min'):\n    if mode=='min':\n        t  = int(t)/60\n        hr = t//60\n        min = t%60\n        return '%2d hr %02d min'%(hr,min)\n    elif mode=='sec':\n        t   = int(t)\n        min = t//60\n        sec = t%60\n        return '%2d min %02d sec'%(min,sec)\n    else: raise NotImplementedError\n        \ndef compute_kaggle_metric( predict, coupling_value, coupling_type):\n    mae     = [None]*NUM_COUPLING_TYPE\n    log_mae = [None]*NUM_COUPLING_TYPE\n    predict=((predict*targetStd)+targetMean)\n    diff = np.fabs(predict-coupling_value)\n    for t in range(NUM_COUPLING_TYPE):\n        index = np.where(coupling_type==t)[0]\n        if len(index)>0:\n            m = diff[index].mean()\n            log_m = np.log(m+1e-8)\n            mae[t] = m\n            log_mae[t] = log_m\n        else: pass\n    return mae, log_mae\n\nCOUPLING_TYPE_STATS=[\n    #type   #mean, std, min, max\n    '1JHC',  94.9761528641869,   18.27722399839607,   66.6008,   204.8800,\n    '2JHC',  -0.2706244378832,    4.52360876732858,  -36.2186,    42.8192,\n    '3JHC',   3.6884695895355,    3.07090647005439,  -18.5821,    76.0437,\n    '1JHN',  47.4798844844683,   10.92204561670947,   24.3222,    80.4187,\n    '2JHN',   3.1247536134185,    3.67345877025737,   -2.6209,    17.7436,\n    '3JHN',   0.9907298624944,    1.31538940138001,   -3.1724,    10.9712,\n    '2JHH', -10.2866051639817,    3.97960190019757,  -35.1761,    11.8542,\n    '3JHH',   4.7710233597359,    3.70498129755812,   -3.0205,    17.4841,\n]\n\nNUM_COUPLING_TYPE = len(COUPLING_TYPE_STATS)//5\nCOUPLING_TYPE_MEAN = [ COUPLING_TYPE_STATS[i*5+1] for i in range(NUM_COUPLING_TYPE)]\nCOUPLING_TYPE_STD  = [ COUPLING_TYPE_STATS[i*5+2] for i in range(NUM_COUPLING_TYPE)]\nCOUPLING_TYPE      = [ COUPLING_TYPE_STATS[i*5  ] for i in range(NUM_COUPLING_TYPE)]\n\ndef read_pickle_from_file(pickle_file):\n    with open(pickle_file, 'rb') as f:\n        return(pickle.load(f))\n    \nclass Logger(object):\n    def __init__(self):\n        self.terminal = sys.stdout  #stdout\n        self.file = None\n\n    def open(self, file, mode=None):\n        if mode is None: mode ='w'\n        self.file = open(file, mode)\n\n    def write(self, message, is_terminal=1, is_file=1 ):\n        if '\\r' in message: is_file=0\n\n        if is_terminal == 1:\n            self.terminal.write(message)\n            self.terminal.flush()\n            #time.sleep(1)\n\n        if is_file == 1:\n            self.file.write(message)\n            self.file.flush()\n\n    def flush(self):\n        # this flush method is needed for python 3 compatibility.\n        # this handles the flush command by doing nothing.\n        # you might want to specify some extra behavior here.\n        pass\n    \ndef do_valid(net, valid_loader):\n\n    valid_num = 0\n    valid_predict = []\n    valid_coupling_type  = []\n    valid_coupling_value = []\n\n    valid_loss = 0\n    for b, (node, edge, edge_index, node_index, coupling_value, coupling_index, infor,secondLayerFeat) in enumerate(valid_loader):\n\n        net.eval()\n        node = node.cuda()\n        edge = edge.cuda()\n        edge_index = edge_index.cuda()\n        node_index = node_index.cuda()\n\n        coupling_value = coupling_value.cuda()\n        coupling_index = coupling_index.cuda()\n\n        with torch.no_grad():\n            predict = net(node, edge, edge_index, node_index, coupling_index,secondLayerFeat)\n            loss = criterion(predict, coupling_value)\n\n        #---\n        batch_size = len(infor)\n        valid_predict.append(predict.data.cpu().numpy())\n        valid_coupling_type.append(coupling_index[:,2].data.cpu().numpy())\n        valid_coupling_value.append(coupling_value.data.cpu().numpy())\n\n        valid_loss += batch_size*loss.item()\n        valid_num  += batch_size\n\n        print('\\r %8d /%8d'%(valid_num, len(valid_loader.dataset)),end='',flush=True)\n\n        pass  #-- end of one data loader --\n    assert(valid_num == len(valid_loader.dataset))\n    valid_loss = valid_loss/valid_num\n\n    #compute\n    predict = np.concatenate(valid_predict)\n    coupling_value = np.concatenate(valid_coupling_value)\n    coupling_type  = np.concatenate(valid_coupling_type).astype(np.int32)\n    mae, log_mae   = compute_kaggle_metric( predict, coupling_value, coupling_type,)\n\n    num_target = NUM_COUPLING_TYPE\n    for t in range(NUM_COUPLING_TYPE):\n        if mae[t] is None:\n            mae[t] = 0\n            log_mae[t]  = 0\n            num_target -= 1\n\n    mae_mean, log_mae_mean = np.sum(mae)/num_target, np.sum(log_mae)/num_target\n\n    valid_loss = log_mae + [valid_loss,mae_mean, log_mae_mean]\n    return valid_loss\n\nclass Struct(object):\n    def __init__(self, is_copy=False, **kwargs):\n        self.add(is_copy, **kwargs)\n\n    def add(self, is_copy=False, **kwargs):\n        #self.__dict__.update(kwargs)\n\n        if is_copy == False:\n            for key, value in kwargs.items():\n                setattr(self, key, value)\n        else:\n            for key, value in kwargs.items():\n                try:\n                    setattr(self, key, copy.deepcopy(value))\n                    #setattr(self, key, value.copy())\n                except Exception:\n                    setattr(self, key, value)\n    def __str__(self):\n        return str(self.__dict__.keys())\n    \n#############################################################################\n\nclass LinearBn(nn.Module):\n    def __init__(self, in_channel, out_channel, act=None):\n        super(LinearBn, self).__init__()\n        self.linear = nn.Linear(in_channel, out_channel, bias=False)\n        self.bn   = nn.BatchNorm1d(out_channel,eps=1e-05, momentum=0.5) #increase momentum\n        self.act  = act\n\n    def forward(self, x):\n        x = self.linear(x)\n        if self.bn is not None:\n            x = self.bn(x)\n        if self.act is not None:\n            x = self.act(x)\n        return x\n\nclass GraphConv(nn.Module):\n    def __init__(self, node_dim, edge_dim ):\n        super(GraphConv, self).__init__()\n        self.node_dim = 128\n        self.encoder = nn.Sequential(\n            LinearBn(edge_dim, 256),\n            nn.ReLU(inplace=True),\n            LinearBn(256, 256),\n            nn.ReLU(inplace=True),\n            LinearBn(256, 128),\n            nn.ReLU(inplace=True),\n            LinearBn(128, self.node_dim * self.node_dim)\n        )\n\n        self.gru  = nn.GRU(self.node_dim, self.node_dim, batch_first=False, bidirectional=False)\n        self.bias = nn.Parameter(torch.Tensor(self.node_dim))\n        self.bias.data.uniform_(-1.0 / math.sqrt(self.node_dim), 1.0 / math.sqrt(self.node_dim))\n\n\n    def forward(self, node, edge_index, edge, hidden):\n        num_node, node_dim = node.shape\n        num_edge, edge_dim = edge.shape\n        edge_index = edge_index.t().contiguous()\n        self.node_dim = node_dim\n\n        #1. message :  m_j = SUM_i f(n_i, n_j, e_ij)  where i is neighbour(j)\n        x_i     = torch.index_select(node, 0, edge_index[0])\n        edge    = self.encoder(edge).view(-1,node_dim,node_dim)\n        #message = x_i.view(-1,node_dim,1)*edge\n        #message = message.sum(1)\n        message = x_i.view(-1,1,node_dim)@edge\n        message = message.view(-1,node_dim)\n        message = scatter_('mean', message, edge_index[1], dim_size=num_node)\n        message = F.relu(message +self.bias)\n\n        #2. update: n_j = f(n_j, m_j)\n        update = message\n\n        #batch_first=True\n        update, hidden = self.gru(update.view(1,-1,node_dim), hidden)\n        update = update.view(-1,node_dim)\n\n        return update, hidden\n\nclass Set2Set(torch.nn.Module):\n\n    def softmax(self, x, index, num=None):\n        x = x -  scatter_max(x, index, dim=0, dim_size=num)[0][index]\n        x = x.exp()\n        x = x / (scatter_add(x, index, dim=0, dim_size=num)[index] + 1e-16)\n        return x\n\n    def __init__(self, in_channel, processing_step=1):\n        super(Set2Set, self).__init__()\n        num_layer = 1\n\n        self.processing_step = processing_step\n        self.in_channel  = 128\n        in_channel = self.in_channel\n        self.out_channel = 2 * self.in_channel\n        out_channel = self.out_channel\n        self.num_layer   = num_layer\n        self.lstm = torch.nn.LSTM(out_channel, in_channel, num_layer)\n        self.lstm.reset_parameters()\n\n    def forward(self, x, batch_index):\n        batch_size = batch_index.max().item() + 1\n        h = (x.new_zeros((self.num_layer, batch_size, self.in_channel)),\n             x.new_zeros((self.num_layer, batch_size, self.in_channel)))\n        q_star = x.new_zeros(batch_size, self.out_channel)\n        for i in range(self.processing_step):\n            q, h = self.lstm(q_star.unsqueeze(0), h)\n            q = q.view(batch_size, -1)\n\n            e = (x * q[batch_index]).sum(dim=-1, keepdim=True) #shape = num_node x 1\n            a = self.softmax(e, batch_index, num=batch_size)   #shape = num_node x 1\n            r = scatter_add(a * x, batch_index, dim=0, dim_size=batch_size) #apply attention #shape = batch_size x ...\n            q_star = torch.cat([q, r], dim=-1)\n        return q_star\n    \n\n#message passing\nclass Net(torch.nn.Module):\n    def __init__(self, node_dim=13, edge_dim=5, num_target=1):\n        super(Net, self).__init__()\n        #very important hyper parameter - controls how many times messages are sent between the nodes\n        self.num_propagate = NUM_PROPAGATE\n        self.num_s2s = NUM_S2S\n        #needs to be changed to the number of node features\n        self.node_dim = node_dim\n        \n        self.preprocess = nn.Sequential(\n            LinearBn(self.node_dim, 128),#128\n            nn.ReLU(inplace=True),\n            LinearBn(128, 128),\n            nn.ReLU(inplace=True),\n        )\n        #140 x 13\n        self.propagate = GraphConv(128, edge_dim)\n        self.set2set = Set2Set(128, processing_step=self.num_s2s)\n\n        #predict coupling constant\n        self.predict = nn.Sequential(\n            #[pool,node0,node1,secondLayerFeat] - pool is 128*2 in size, and node0/node1 are both 128 in size.  The secondLayerFeat \n            #are the features from the training set used to predict just the scalar coupling constant and are 44 in size.\n            LinearBn(128*4+len(LGB_COLS), 1024),\n            nn.ReLU(inplace=True),\n            LinearBn( 1024, 512),\n            nn.ReLU(inplace=True),\n            nn.Linear(512, 1),\n        )\n\n    def forward(self, node, edge, edge_index, node_index, coupling_index,secondLayerFeat):\n        num_node, node_dim = node.shape\n        num_edge, edge_dim = edge.shape\n        self.node_dim = node_dim\n        node   = self.preprocess(node)\n        hidden = node.view(1,num_node,-1)\n        for i in range(self.num_propagate):\n            node, hidden =  self.propagate(node, edge_index, edge, hidden)\n        pool = self.set2set(node, node_index)\n\n        #---\n        num_coupling = len(coupling_index)\n        coupling_atom0_index, coupling_atom1_index, coupling_type_index, coupling_batch_index = torch.split(coupling_index,1,dim=1)\n        \n        pool  = torch.index_select( pool, dim=0, index=coupling_batch_index.view(-1))\n        node0 = torch.index_select( node, dim=0, index=coupling_atom0_index.view(-1))\n        node1 = torch.index_select( node, dim=0, index=coupling_atom1_index.view(-1))\n        \n        device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\n        secondLayerFeat=secondLayerFeat[:,LGB_COLS].to(device)\n        #backend CUDA\n        predict = self.predict(torch.cat([pool,node0,node1,secondLayerFeat],-1))\n        return predict.view(-1)\n\ndef criterion(predict, truth):\n    predict = predict.view(-1)\n    truth   = truth.view(-1)\n    truth=(truth-targetMean)/targetStd\n    assert(predict.shape==truth.shape)\n\n    loss = torch.abs(predict-truth)\n    loss = loss.mean()\n    loss = torch.log(loss)\n    return loss\n\nclass ChampsDataset(Dataset):\n    def __init__(self,molecules, split, csv, mode, pickle_file, augment=None):\n        self.split, self.csv, self.mode, self.augment = split, csv, mode, augment\n        self.graphHolder = np.load(GRAPH_DIR + pickle_file, allow_pickle=True)\n        if mode =='train': self.graphHolder = [self.graphHolder[i] for i in range(len(self.graphHolder)) if ALL_TRAIN_MOLECULES[i] in molecules]\n        self.df = pd.read_csv(DATA_DIR + '%s.csv'%csv)\n        self.df = self.df[self.df[\"molecule_name\"].isin(molecules)]\n        if split is not None: self.id = np.load(SPLIT_DIR + '%s'%split, allow_pickle=True)\n        else: self.id = self.df.molecule_name.unique()\n\n    def __len__(self):\n        return len(self.id)\n    \n    def __getitem__(self, index):\n        molecule_name = self.id[index]\n        graph = copy.copy(self.graphHolder[index])\n        assert(graph.molecule_name==molecule_name)\n\n        ##filter only J link\n        # 1JHC,     2JHC,     3JHC,     1JHN,     2JHN,     3JHN,     2JHH,     3JHH\n        mask = np.zeros(len(graph.coupling.type),np.bool)\n        for t in [TYPE_TO_TRAIN]:\n            mask += (graph.coupling.type == COUPLING_TYPE.index(t))\n        \n        graph.coupling.id = graph.coupling.id [mask]\n        graph.coupling.contribution = graph.coupling.contribution [mask]\n        graph.coupling.index = graph.coupling.index [mask]\n        graph.coupling.type = graph.coupling.type [mask]\n        graph.coupling.value = graph.coupling.value [mask]\n        graph.coupling.secondLayerFeat = graph.coupling.secondLayerFeat [mask]\n        graph.node = np.concatenate(graph.node,-1)\n        graph.node = graph.node[:,NODE_COLS]\n        graph.edge = np.concatenate(graph.edge,-1)\n        return graph\n\ndef null_collate(batch):\n    batch_size = len(batch)\n    node, edge, node_index, edge_index = [], [], [], []\n    coupling_value,coupling_atom_index,coupling_type_index,coupling_batch_index = [],[],[],[]\n    secondLayerFeat,infor=[],[]\n    offset = 0\n    for b in range(batch_size):\n        graph = batch[b]\n        num_node = len(graph.node)\n        node.append(graph.node)\n        edge.append(graph.edge)\n        edge_index.append(graph.edge_index+offset)\n        node_index.append(np.array([b]*num_node))\n\n        num_coupling = len(graph.coupling.value)\n        coupling_value.append(graph.coupling.value)\n        coupling_atom_index.append(graph.coupling.index+offset)\n        coupling_type_index.append (graph.coupling.type)\n        coupling_batch_index.append(np.array([b]*num_coupling))\n        secondLayerFeat.append(graph.coupling.secondLayerFeat)\n\n        infor.append((graph.molecule_name, graph.smiles, graph.coupling.id))\n        offset += num_node\n\n    node = torch.from_numpy(np.concatenate(node)).float()\n    edge = torch.from_numpy(np.concatenate(edge)).float()\n    edge_index = torch.from_numpy(np.concatenate(edge_index).astype(np.int32)).long()\n    node_index = torch.from_numpy(np.concatenate(node_index)).long()\n    \n    coupling_value = torch.from_numpy(np.concatenate(coupling_value)).float()\n    coupling_index = np.concatenate([\n        np.concatenate(coupling_atom_index),\n        np.concatenate(coupling_type_index).reshape(-1,1),\n        np.concatenate(coupling_batch_index).reshape(-1,1)\n    ],-1)\n    \n    secondLayerFeat = torch.from_numpy(np.concatenate(secondLayerFeat)).float()\n    \n    coupling_index = torch.from_numpy(coupling_index).long()\n    return node, edge, edge_index, node_index, coupling_value, coupling_index, infor, secondLayerFeat\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Make sure to change type and number from 3JHN and 36619"},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Loading data...')\ninitial_checkpoint = None\nscheduler = NullScheduler(lr=STARTING_LR)\n#scheduler = DecayScheduler(base_lr=STARTING_LR,decay=.5,step=25)\n## setup  -----------------------------------------------------------------------------\nbatch_size = BATCH_SIZE \ntrain=pd.read_csv(\"../input/champs-scalar-coupling/train.csv\")\n\nALL_TRAIN_MOLECULES = train[\"molecule_name\"].unique()\nmols=train.loc[train[\"type\"]==TYPE_TO_TRAIN,\"molecule_name\"].unique()\n\nkf = KFold(n_splits=N_FOLD, shuffle=True, random_state=KFOLD_SEED)\nfor ifold, (idx_tr, idx_va) in enumerate(kf.split(mols)):\n    if ifold == FOLD_TO_TRAIN: break\n\ntrain_dataset = ChampsDataset(molecules=mols[idx_tr],csv='train',mode ='train', pickle_file=GRAPH_TRAIN_PICKLE,split=None,augment=None)\ntrain_loader  = DataLoader(train_dataset, sampler = RandomSampler(train_dataset),batch_size=batch_size,drop_last=True,num_workers=4,pin_memory=True,collate_fn=null_collate)\nvalid_dataset = ChampsDataset(molecules=mols[idx_va],csv='train',mode='train', pickle_file=GRAPH_TRAIN_PICKLE, split=None,augment=None)\nvalid_loader = DataLoader(valid_dataset,sampler=RandomSampler(valid_dataset),batch_size=batch_size,drop_last=False,num_workers=4,pin_memory=True,collate_fn=null_collate)\n\nEDGE_DIM = np.sum([train_dataset.graphHolder[0].edge[j].shape[1] for j in range(len(train_dataset.graphHolder[0].edge))])\nNODE_DIM = np.sum([train_dataset.graphHolder[0].node[j].shape[1] for j in range(len(train_dataset.graphHolder[0].node))])\n\nassert(len(train_dataset)>=batch_size)\nprint('Loading done!')\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## net ----------------------------------------\nnet = Net(node_dim=NODE_DIM,edge_dim=EDGE_DIM, num_target=NUM_TARGET).cuda()\nif initial_checkpoint is not None:\n    net.load_state_dict(torch.load(initial_checkpoint, map_location=lambda storage, loc: storage))\n    \noptimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, net.parameters()),lr=STARTING_LR)\n\niter_accum  = 1\nnum_iters   = NUM_ITERS\n\niter_smooth = 50\niter_log    = 500\niter_valid  = 500\niter_save   = [0, num_iters-1]+ list(range(0, num_iters, 5000))#1*1000\nstart_iter = 0\nstart_epoch= 0\nrate       = 0\n\ntrain_loss   = np.zeros(20,np.float32)\nvalid_loss   = np.zeros(20,np.float32)\nbatch_loss   = np.zeros(20,np.float32)\niter = 0\ni    = 0\nstart = timer()\n\nbestLoss, bestDict, bestEpoch = 9999, 0, 0\nprint('Create Net done.')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"targetMean=np.mean(train.loc[train[\"type\"]==TYPE_TO_TRAIN,\"scalar_coupling_constant\"])\ntargetStd=np.std(train.loc[train[\"type\"]==TYPE_TO_TRAIN,\"scalar_coupling_constant\"])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"log = Logger()\nlog.open('log_train.txt',mode='a')\n## start training here! ##############################################\nlog.write('** start training here! **\\n')\nlog.write('   batch_size =%d,  iter_accum=%d\\n'%(batch_size,iter_accum))\nlog.write('                      |--------------- VALID ----------------------------------------------------------------|-- TRAIN/BATCH ---------\\n')\nlog.write('                      |std %4.1f    %4.1f    %4.1f    %4.1f    %4.1f    %4.1f    %4.1f   %4.1f  |                    |        | \\n'%tuple(COUPLING_TYPE_STD))\nlog.write('rate     iter   epoch |    1JHC,   2JHC,   3JHC,   1JHN,   2JHN,   3JHN,   2JHH,   3JHH |  loss  mae log_mae | loss   | time          \\n')\nlog.write('--------------------------------------------------------------------------------------------------------------------------------------\\n')\nrate=STARTING_LR\ntrain_loss=5\nwhile  iter<=num_iters:\n    sum_train_loss = np.zeros(20,np.float32)\n    sum = 0\n\n    optimizer.zero_grad()\n    for node, edge, edge_index, node_index, coupling_value, coupling_index, infor, secondLayerFeat in train_loader:\n\n        batch_size = len(infor)\n        iter  = i + start_iter\n        epoch = (iter-start_iter)*batch_size/len(train_dataset) + start_epoch   \n\n        if (iter % iter_valid==0):\n            valid_loss = do_valid(net, valid_loader) #\n            #reduceScheduler.step(valid_loss[8])\n            if valid_loss[10]<bestLoss:\n                bestEpoch=epoch\n                bestLoss=valid_loss[10]\n                bestDict=copy.deepcopy(net.state_dict())\n        \n        if (iter % iter_log==0):\n            print('\\r',end='',flush=True)\n            asterisk = '*' if iter in iter_save else ' '\n            log.write('%0.5f  %5.1f%s %5.1f |  %+0.3f, %+0.3f, %+0.3f, %+0.3f, %+0.3f, %+0.3f, %+0.3f, %+0.3f | %+5.3f %5.2f %+0.2f | %+5.3f | %s' % (\\\n                             rate, iter/1000, asterisk, epoch, *valid_loss[:11], train_loss, time_to_str((timer() - start),'min')))\n            log.write('\\n')\n            \n        if iter in iter_save:\n                    torch.save(net.state_dict(),'model.pth')\n                    torch.save({\n                        'optimizer': optimizer.state_dict(),\n                        'iter'     : iter,\n                        'epoch'    : epoch,\n                    },'optimizer.pth')\n\n        rate=scheduler.get_rate(epoch)\n        adjust_learning_rate(optimizer, rate)\n        \n        net.train()  \n        node = node.cuda()\n        edge = edge.cuda()\n        edge_index = edge_index.cuda()\n        node_index = node_index.cuda()\n        coupling_value = coupling_value.cuda()\n        coupling_index = coupling_index.cuda()\n\n        predict = net(node, edge, edge_index, node_index, coupling_index, secondLayerFeat)\n        loss = criterion(predict, coupling_value)\n        \n        (loss/iter_accum).backward()\n        if (iter % iter_accum)==0:\n            optimizer.step()\n            optimizer.zero_grad()\n\n        # print statistics  ------------\n        batch_loss[:1] = [loss.item()]\n        sum_train_loss += batch_loss\n        sum += 1\n        if iter%iter_smooth == 0:\n            train_loss = loss\n            sum_train_loss = np.zeros(20,np.float32)\n            sum = 0\n\n        i += 1\n        pass  \n    pass ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"log.write(\"lr:\"+str(scheduler.get_rate(epoch)))\nprint(\"bestLoss: \"+str(bestLoss))\nprint(\"bestEpoch: \"+str(bestEpoch))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"net.load_state_dict(bestDict) \ntest=pd.read_csv(\"../input/champs-scalar-coupling/test.csv\")\n#test_mols=test.loc[test[\"type\"]==TYPE_TO_TRAIN,\"molecule_name\"].unique()\ntest_mols=test[\"molecule_name\"].unique()\ntest_dataset = ChampsDataset(molecules=test_mols,csv='test',mode='test',pickle_file=GRAPH_TEST_PICKLE,split=None,augment=None,)\ntest_loader = DataLoader(test_dataset,sampler=SequentialSampler(test_dataset),batch_size=BATCH_SIZE,drop_last=False,num_workers=4,pin_memory=True,collate_fn=null_collate)\n\ntest_num = 0\ntest_predict = []\ntest_coupling_type  = []\ntest_coupling_value = []\ntest_id = []\ntest_loss = 0\n\nstart = timer()\nfor b, (node, edge, edge_index, node_index, coupling_value, coupling_index, infor,secondLayerFeat) in enumerate(test_loader):\n    net.eval()\n    with torch.no_grad():\n        node = node.cuda()\n        edge = edge.cuda()\n        edge_index = edge_index.cuda()\n        node_index = node_index.cuda()\n        coupling_index = coupling_index.cuda()\n        coupling_value = coupling_value.cuda()\n        predict = net(node, edge, edge_index, node_index, coupling_index,secondLayerFeat)\n        loss = criterion(predict, coupling_value)\n\n        #---\n    batch_size = len(infor)\n    test_id.extend(list(np.concatenate([infor[b][2] for b in range(batch_size)])))\n    test_predict.append(predict.data.cpu().numpy())\n    test_coupling_type.append(coupling_index[:,2].data.cpu().numpy())\n    test_coupling_value.append(coupling_value.data.cpu().numpy())\n    test_loss += loss.item()*batch_size\n    test_num += batch_size\n\n    print('\\r %8d/%8d     %0.2f  %s'%(\n        test_num, len(test_dataset),test_num/len(test_dataset),\n        time_to_str(timer()-start,'min')),end='',flush=True)\n    pass  #-- end of one data loader --\n\nassert(test_num == len(test_dataset))\nprint('\\n')\n\nid  = test_id\npredict  = np.concatenate(test_predict)\npredict=((predict*targetStd)+targetMean)\ndf = pd.DataFrame(list(zip(id, predict)), columns = ['id', 'scalar_coupling_constant'])\n\nfilename = \"submission_\" + TYPE_TO_TRAIN + '_batchsize' + str(BATCH_SIZE) + '_nbiters' + str(NUM_ITERS) + '_seed' + str(KFOLD_SEED) + '_fold' + str(FOLD_TO_TRAIN) + '_bestLoss' + str(np.round(bestLoss, 5))\ndf.to_csv(filename+\".csv\",index=False)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}