Split Up: dtreevis (Part 2)

Goal

This post aims to break down the module dtreeviz module step by step to fully understand what is implemented. After fully understanding this, I would like to contribute to this module and submit a pull request.

I really like this module and would like to see this works for other tree-based modules like XGBoost or Lightgbm. I found the exact same issue (issues 15) in github so I hope I could contribute to this issue.

This post is the 2nd part of the process of breaking down ShadowDecTree.

Reference

ShadowDecTree class

Source github

In [109]:
import numpy as np
import pandas as pd
from collections import defaultdict, Sequence
from typing import Mapping, List, Tuple
from numbers import Number
from sklearn.utils import compute_class_weight
from dtreeviz.shadow import ShadowDecTreeNode 


class ShadowDecTree:
    """
    The decision trees for classifiers and regressors from scikit-learn
    are built for efficiency, not ease of tree walking. This class
    is intended as a way to wrap all of that information in an easy to use
    package.
    This tree shadows a decision tree as constructed by scikit-learn's
    DecisionTree(Regressor|Classifier).  As part of build process, the
    samples considered at each decision node or at each leaf node are
    saved as a big dictionary for use by the nodes.
    Field leaves is list of shadow leaf nodes. Field internal is list of
    shadow non-leaf nodes.
    Field root is the shadow tree root.
    Parameters
    ----------
    class_names : (List[str],Mapping[int,str]). A mapping from target value
                  to target class name. If you pass in a list of strings,
                  target value i must be associated with class name[i]. You
                  can also pass in a dict that maps value to name.
    """
    def __init__(self, tree_model,
                 X_train,
                 y_train,
                 feature_names : List[str],
                 class_names : (List[str],Mapping[int,str])=None):
        self.tree_model = tree_model
        self.feature_names = feature_names
        self.class_names = class_names
        self.class_weight = tree_model.class_weight

        if getattr(tree_model, 'tree_') is None: # make sure model is fit
            tree_model.fit(X_train, y_train)

        if tree_model.tree_.n_classes > 1:
            if isinstance(self.class_names, dict):
                self.class_names = self.class_names
            elif isinstance(self.class_names, Sequence):
                self.class_names = {i:n for i, n in enumerate(self.class_names)}
            else:
                raise Exception(f"class_names must be dict or sequence, not {self.class_names.__class__.__name__}")

        if isinstance(X_train, pd.DataFrame):
            X_train = X_train.values
        self.X_train = X_train
        if isinstance(y_train, pd.Series):
            y_train = y_train.values
        self.y_train = y_train
        self.node_to_samples = ShadowDecTree.node_samples(tree_model, X_train)
        if self.isclassifier():
            self.unique_target_values = np.unique(y_train)
            self.class_weights = compute_class_weight(tree_model.class_weight, self.unique_target_values, self.y_train)

        tree = tree_model.tree_
        children_left = tree.children_left
        children_right = tree.children_right

        # use locals not args to walk() for recursion speed in python
        leaves = []
        internal = [] # non-leaf nodes

        def walk(node_id):
            if (children_left[node_id] == -1 and children_right[node_id] == -1):  # leaf
                t = ShadowDecTreeNode(self, node_id)
                leaves.append(t)
                return t
            else:  # decision node
                left = walk(children_left[node_id])
                right = walk(children_right[node_id])
                t = ShadowDecTreeNode(self, node_id, left, right)
                internal.append(t)
                return t

        root_node_id = 0
        # record root to actual shadow nodes
        self.root = walk(root_node_id)
        self.leaves = leaves
        self.internal = internal

    def nclasses(self):
        return self.tree_model.tree_.n_classes[0]

    def nnodes(self) -> int:
        "Return total nodes in the tree"
        return self.tree_model.tree_.node_count

    def leaf_sample_counts(self) -> List[int]:
        return [self.tree_model.tree_.n_node_samples[leaf.id] for leaf in self.leaves]

    def isclassifier(self):
        return self.tree_model.tree_.n_classes > 1

    def get_split_node_heights(self, X_train, y_train, nbins) -> Mapping[int,int]:
        class_values = self.unique_target_values
        node_heights = {}
        # print(f"Goal {nbins} bins")
        for node in self.internal:
            # print(node.feature_name(), node.id)
            X_feature = X_train[:, node.feature()]
            overall_feature_range = (np.min(X_feature), np.max(X_feature))
            # print(f"range {overall_feature_range}")
            r = overall_feature_range[1] - overall_feature_range[0]

            bins = np.linspace(overall_feature_range[0],
                               overall_feature_range[1], nbins+1)
            # bins = np.arange(overall_feature_range[0],
            #                  overall_feature_range[1] + binwidth, binwidth)
            # print(f"\tlen(bins)={len(bins):2d} bins={bins}")
            X, y = X_feature[node.samples()], y_train[node.samples()]
            X_hist = [X[y == cl] for cl in class_values]
            height_of_bins = np.zeros(nbins)
            for cl in class_values:
                hist, foo = np.histogram(X_hist[cl], bins=bins, range=overall_feature_range)
                # print(f"class {cl}: goal_n={len(bins):2d} n={len(hist):2d} {hist}")
                height_of_bins += hist
            node_heights[node.id] = np.max(height_of_bins)

            # print(f"\tmax={np.max(height_of_bins):2.0f}, heights={list(height_of_bins)}, {len(height_of_bins)} bins")
        return node_heights

    def predict(self, x : np.ndarray) -> Tuple[Number,List]:
        """
        Given an x-vector of features, return predicted class or value based upon
        this tree. Also return path from root to leaf as 2nd value in return tuple.
        Recursively walk down tree from root to appropriate leaf by
        comparing feature in x to node's split value. Also return
        :param x: Feature vector to run down the tree to a leaf.
        :type x: np.ndarray
        :return: Predicted class or value based
        :rtype: Number
        """
        def walk(t, x, path):
            if t is None:
                return None
            path.append(t)
            if t.isleaf():
                return t
            if x[t.feature()] < t.split():
                return walk(t.left, x, path)
            return walk(t.right, x, path)

        path = []
        leaf = walk(self.root, x, path)
        return leaf.prediction(), path

    def tesselation(self):
        """
        Walk tree and return list of tuples containing a leaf node and bounding box
        list of (x1,y1,x2,y2) coordinates
        :return:
        :rtype:
        """
        bboxes = []

        def walk(t, bbox):
            if t is None:
                return None
            # print(f"Node {t.id} bbox {bbox} {'   LEAF' if t.isleaf() else ''}")
            if t.isleaf():
                bboxes.append((t, bbox))
                return t
            # shrink bbox for left, right and recurse
            s = t.split()
            if t.feature()==0:
                walk(t.left,  (bbox[0],bbox[1],s,bbox[3]))
                walk(t.right, (s,bbox[1],bbox[2],bbox[3]))
            else:
                walk(t.left,  (bbox[0],bbox[1],bbox[2],s))
                walk(t.right, (bbox[0],s,bbox[2],bbox[3]))

        # create bounding box in feature space (not zeroed)
        f1_values = self.X_train[:, 0]
        f2_values = self.X_train[:, 1]
        overall_bbox = (np.min(f1_values), np.min(f2_values), # x,y of lower left edge
                        np.max(f1_values), np.max(f2_values)) # x,y of upper right edge
        walk(self.root, overall_bbox)

        return bboxes

    @staticmethod
    def node_samples(tree_model, data) -> Mapping[int, list]:
        """
        Return dictionary mapping node id to list of sample indexes considered by
        the feature/split decision.
        """
        # Doc say: "Return a node indicator matrix where non zero elements
        #           indicates that the samples goes through the nodes."
        dec_paths = tree_model.decision_path(data)

        # each sample has path taken down tree
        node_to_samples = defaultdict(list)
        for sample_i, dec in enumerate(dec_paths):
            _, nz_nodes = dec.nonzero()
            for node_id in nz_nodes:
                node_to_samples[node_id].append(sample_i)

        return node_to_samples

    def __str__(self):
        return str(self.root)

Instantiate class objects

Create a tree model by scikit learn

In [93]:
import numpy as np
import graphviz 
from sklearn import tree

X = np.array([[0, 0], [1, 1]])
Y = np.array([0, 1])
# Y = [0, 1]
clf = tree.DecisionTreeClassifier()
clf = clf.fit(X, Y)
dot_data = tree.export_graphviz(clf, out_file=None, 
                     feature_names=[0, 1],  
                     class_names=['0', '1'],  
                     filled=True, rounded=True,  
                     special_characters=True)  
graph = graphviz.Source(dot_data)  
graph 
Out[93]:
Tree 0 1 ≤ 0.5gini = 0.5samples = 2value = [1, 1]class = 01 gini = 0.0samples = 1value = [1, 0]class = 00->1 True2 gini = 0.0samples = 1value = [0, 1]class = 10->2 False

Create a ShadowDecTree

ShadowDecTree __init__

  • L33-41: define __initi__ with 5 input arguments.
  • L38-41: store the input arguments as a class member
  • L43-44: check if the trained model exists in tree_model, and if not, it enforces to train the tree model.

  • L46-52: treatment for multi label classification

  • L54-59: treatment for pandas if pandas.DataFrame is used for X_train and y_train. Convert them into np.array

  • L60: a static method node_samples in ShadowDecTree to create a map from node id in tree_model to list of sample indices.

  • L61-63: treatment for target values and class weights if tree_model is for classification

  • L65-71: preparation for re-organizing tree object into the one for dtreeviz

  • L73-83: define the recursive function to walk through nodes by post order traversal through Depth-First Search (DFS) algorithm.

  • L85-89: execute walk method from the root node. Store a list of end nodes as leaves and a list of intermediate nodes as internal.

In [94]:
# instantiate ShadowDecTree
shadow_tree = ShadowDecTree(tree_model=clf, X_train=X, y_train=Y, feature_names=[0, 1], class_names=[0, 1])
In [95]:
# A root node
shadow_tree.root
Out[95]:
<__main__.ShadowDecTreeNode at 0x1216dd4a8>
In [96]:
# A list of end nodes 
shadow_tree.leaves
Out[96]:
[<__main__.ShadowDecTreeNode at 0x121696470>,
 <__main__.ShadowDecTreeNode at 0x1216dd940>]
In [97]:
# A list of internal nodes
shadow_tree.internal
Out[97]:
[<__main__.ShadowDecTreeNode at 0x1216dd4a8>]
In [98]:
# A mapping from node id to sample id
shadow_tree.node_to_samples
Out[98]:
defaultdict(list, {0: [0, 1], 1: [0], 2: [1]})

Other methods for ShadowDecTree

In [99]:
# L91 nclasses
shadow_tree.nclasses()
Out[99]:
2
In [100]:
# L94 nnodes
shadow_tree.nnodes()
Out[100]:
3
In [101]:
# L98 leaf_sample_counts
shadow_tree.leaf_sample_counts()
Out[101]:
[1, 1]
In [102]:
# L101 isclassifier
shadow_tree.isclassifier()
Out[102]:
array([ True])
In [103]:
# L104 get_split_node_heights
nbins = 2
shadow_tree.get_split_node_heights(X_train=X, y_train=Y, nbins=nbins)
Out[103]:
{0: 1.0}
In [104]:
print(f"shadow_tree.internal[0].feature(): {shadow_tree.internal[0].feature()}")
X[:, shadow_tree.internal[0].feature()]
shadow_tree.internal[0].feature(): 1
Out[104]:
array([0, 1])
In [105]:
# L132 predict
shadow_tree.predict(np.array([0, 0.5]))
Out[105]:
(1,
 [<__main__.ShadowDecTreeNode at 0x1216dd4a8>,
  <__main__.ShadowDecTreeNode at 0x1216dd940>])
In [106]:
# L158 tesselation
shadow_tree.tesselation()
Out[106]:
[(<__main__.ShadowDecTreeNode at 0x121696470>, (0, 0, 1, 0.5)),
 (<__main__.ShadowDecTreeNode at 0x1216dd940>, (0, 0.5, 1, 1))]

Opportunity to contribute

Through line-by-line execution, I found the following opportunities I could potentially contribute to.

  • Add documentation for each methods
  • Add validation if it is np.array or not for X_train and y_train since when I pass the list as X_train and y_train, I got the error for get_split_node_heights and tesselation like below: image

Normalizing Observations

Goal

This post aims to introduce how to normalize the observations including the followings:

  • Min-Max scaling
  • Standard scaling

image

Libraries

In [53]:
import pandas as pd
import numpy as np
from sklearn.preprocessing import minmax_scale, StandardScaler
import matplotlib.pyplot as plt
%matplotlib inline

import warnings
warnings.simplefilter('ignore')

Create a data

In [63]:
df = pd.DataFrame(data=60*np.random.randn(100)+20)
df.describe()
Out[63]:
0
count 100.000000
mean 22.664418
std 54.655875
min -123.482962
25% -13.641444
50% 27.253111
75% 56.597637
max 179.529729
In [64]:
df.hist();
plt.title('Original Data');

Normalizing

Min-Max Scaling

$$x_{min-max\,normalized} =\frac{x - min(x)}{max(x) - min(x)} $$
In [65]:
data_minmax = minmax_scale(df, feature_range=(0, 1))
pd.DataFrame(pd.Series(data_minmax.ravel()).describe())
Out[65]:
0
count 100.000000
mean 0.482314
std 0.180375
min 0.000000
25% 0.362498
50% 0.497458
75% 0.594301
max 1.000000
In [72]:
plt.hist(data_minmax);
plt.title('Min-Max Scaled Data');
plt.axvline(x=np.min(data_minmax), ls=':', c='C0', label='Min');
plt.axvline(x=np.max(data_minmax), ls=':', c='C1', label='Max');
plt.legend();

Standard Scaler

This scaling assumes that the data is sampled from Normal distribution. $$x_{standard\,normalized} = \frac{x - mean(x)}{std(x)}$$

In [67]:
ss = StandardScaler()
ss.fit(df)
data_standard_scaled = ss.transform(df)
In [68]:
pd.DataFrame(pd.Series(data_standard_scaled.ravel()).describe())
Out[68]:
0
count 1.000000e+02
mean 3.552714e-17
std 1.005038e+00
min -2.687426e+00
25% -6.676092e-01
50% 8.437905e-02
75% 6.239799e-01
max 2.884513e+00
In [88]:
plt.axvspan(xmin=np.mean(data_standard_scaled)-3*np.std(data_standard_scaled), xmax=np.mean(data_standard_scaled)+3*np.std(data_standard_scaled), color='red', alpha=0.05,label=r'$Mean \pm 3\sigma$');
plt.hist(data_standard_scaled);
plt.title('Standard Scaled Data');
plt.axvline(x=np.mean(data_standard_scaled), ls='-.', c='red', label='Mean');
plt.legend();

BERT Word Embeddings

Goal

This post aims to introduce how to use BERT word embeddings.

Reference

Libraries

In [2]:
import torch
from pytorch_pretrained_bert import BertTokenizer, BertModel, BertForMaskedLM
import matplotlib.pyplot as plt
%matplotlib inline

Load a pre-trained takenizer model

In [3]:
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
100%|██████████| 231508/231508 [00:00<00:00, 426744.34B/s]

Create a sample text

In [10]:
# text = "This is a sample text"
text = "This is the sample sentence for BERT word embeddings"
marked_text = "[CLS] " + text + " [SEP]"

print (marked_text)
[CLS] This is the sample sentence for BERT word embeddings [SEP]

Tokenization

In [11]:
tokenized_text = tokenizer.tokenize(marked_text)
print (tokenized_text)
['[CLS]', 'this', 'is', 'the', 'sample', 'sentence', 'for', 'bert', 'word', 'em', '##bed', '##ding', '##s', '[SEP]']

Convert tokens to ID

In [12]:
indexed_tokens = tokenizer.convert_tokens_to_ids(tokenized_text)

for tup in zip(tokenized_text, indexed_tokens):
    print(tup)
('[CLS]', 101)
('this', 2023)
('is', 2003)
('the', 1996)
('sample', 7099)
('sentence', 6251)
('for', 2005)
('bert', 14324)
('word', 2773)
('em', 7861)
('##bed', 8270)
('##ding', 4667)
('##s', 2015)
('[SEP]', 102)

Interpretability of prediction for Boston Housing using SHAP

Goal

This post aims to introduce how to interpret the prediction for Boston Housing using shap.

What is SHAP?

SHAP is a module for making a prediction by some machine learning models interpretable, where we can see which feature variables have an impact on the predicted value. In other words, it can calculate SHAP values, i.e., how much the predicted variable would be increased or decreased by a certain feature variable.

Reference

Libraries

In [11]:
import xgboost
import shap
shap.initjs()

Load Boston Housing Dataset

In [3]:
X, y = shap.datasets.boston()
X[:5]
Out[3]:
CRIM ZN INDUS CHAS NOX RM AGE DIS RAD TAX PTRATIO B LSTAT
0 0.00632 18.0 2.31 0.0 0.538 6.575 65.2 4.0900 1.0 296.0 15.3 396.90 4.98
1 0.02731 0.0 7.07 0.0 0.469 6.421 78.9 4.9671 2.0 242.0 17.8 396.90 9.14
2 0.02729 0.0 7.07 0.0 0.469 7.185 61.1 4.9671 2.0 242.0 17.8 392.83 4.03
3 0.03237 0.0 2.18 0.0 0.458 6.998 45.8 6.0622 3.0 222.0 18.7 394.63 2.94
4 0.06905 0.0 2.18 0.0 0.458 7.147 54.2 6.0622 3.0 222.0 18.7 396.90 5.33
In [4]:
y[:5]
Out[4]:
array([24. , 21.6, 34.7, 33.4, 36.2])

Train a predictor by xgboost

In [10]:
d_param = {
    "learning_rate": 0.01
}

model = xgboost.train(params=d_param,
                      dtrain=xgboost.DMatrix(X, label=y), 
                      num_boost_round=100)

Create an explainer

In [12]:
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X)

Outcome of SHAP

Single prediction explainer

The visualization below shows the explanations for one prediction based on i-th data.

  • red: positive impacts on the prediction
  • blue: negative impacts on the prediction
In [19]:
i = 0
shap.force_plot(explainer.expected_value, shap_values[i,:], X.iloc[i,:])
Out[19]:
Visualization omitted, Javascript library not loaded!
Have you run `initjs()` in this notebook? If this notebook was from another user you must also trust this notebook (File -> Trust notebook). If you are viewing this notebook on github the Javascript has been stripped for security. If you are using JupyterLab this error is because a JupyterLab extension has not yet been written.

All prediction explainers

All explainers like the above are plotted in one graph as below.

In [14]:
shap.summary_plot(shap_values, X, plot_type="violin")

Variable importance

This variable importance shown as below simply aggregates the above by computing the sum of the absolute values of shap values for all data points.

In [13]:
shap.summary_plot(shap_values, X, plot_type="bar")

Force Plot

The other way of visualizing shap values are the one to stack all shap values across samples or feature values themselves.

In [16]:
shap.force_plot(explainer.expected_value, shap_values, X)
Out[16]:
Visualization omitted, Javascript library not loaded!
Have you run `initjs()` in this notebook? If this notebook was from another user you must also trust this notebook (File -> Trust notebook). If you are viewing this notebook on github the Javascript has been stripped for security. If you are using JupyterLab this error is because a JupyterLab extension has not yet been written.

Dependency Plot

This plot shows a certain value and its shap value as a scatter plot with the color specified by automatically selected variable, which separates most the certain value and its shap value.

In [25]:
# specify by the index of the features
shap.dependence_plot(ind=12, shap_values=shap_values, features=X)
In [17]:
# specify by the feature name
shap.dependence_plot(ind="RM", shap_values=shap_values, features=X)

Loss Functions in Deep Learning with PyTorch

Goal

This post aims to compare loss functions in deep learning with PyTorch.

The following loss functions are covered in this post:

  • Mean Absolute Error (L1 Loss)
  • Mean Square Error (L2 Loss)
  • Binary Cross Entropy (BCE)
  • Kullback-Leibler divergence (KL divergence)

image

Reference

How to Develop a 1D Generative Adversarial Network From Scratch in PyTorch (Part 2)

Goal

This post is the 2nd part of "How to develop a 1d GAN from scratch in PyTorch", inspired by the blog "Machine Learning Mastery - How to Develop a 1D Generative Adversarial Network From Scratch in Keras" written by Jason Brownlee, PhD. But to learn step-by-step, I will describe the same concept with PyTorch.

This post will cover the followings:

Part 2:

  • Train a Discriminator model

Reference

Libraries

In [94]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

# PyTorch
import torch
from torch import nn
from torch.functional import F 
from torch import optim
from torchviz import make_dot

Functions

Defined funcions in Part 1

In [91]:
# Target function
def f(x):
    return x **2

# Create a discriminator model
# Build a feed-forward network


# define the standalone discriminator model
def define_discriminator(n_inputs=2):
    model = nn.Sequential(nn.Linear(2, 25),
                          nn.ReLU(),
                          nn.Linear(25,2),
                         )
    
    # Loss
    criterion = nn.BCELoss()
    
    

    # Optimizer
    optimizer = optim.Adam(model.parameters(), lr=0.001)
    
    return model, criterion, optimizer




# Generate samples
def generate_samples(size=100, label='real'):
    """Generate samples with real or fake label
    """
    x = np.random.randn(size, 1)
    x2 = f(x)
    
    y = np.ones((size, 1)) * (label == 'real')
    return np.hstack([x, x2]), y
    

Train a discriminator

In [92]:
predicted
Out[92]:
tensor([0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1,
        1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0,
        1, 1])
In [97]:
pred
Out[97]:
tensor([[0.3894, 0.5372],
        [0.3957, 0.5342],
        [0.3676, 0.5435],
        [0.3657, 0.5557],
        [0.4467, 0.5787],
        [0.4409, 0.5637],
        [0.3665, 0.5453],
        [0.4364, 0.6085],
        [0.3539, 0.5852],
        [0.3881, 0.5252],
        [0.4279, 0.5502],
        [0.3708, 0.5621],
        [0.4193, 0.5413],
        [0.3652, 0.5547],
        [0.3911, 0.5338],
        [0.4207, 0.5430],
        [0.4118, 0.5368],
        [0.4456, 0.5914],
        [0.4063, 0.5358],
        [0.3798, 0.5349],
        [0.4364, 0.5584],
        [0.4035, 0.5353],
        [0.3653, 0.5547],
        [0.3659, 0.5461],
        [0.3673, 0.5582],
        [0.4308, 0.5530],
        [0.4217, 0.5444],
        [0.4018, 0.5350],
        [0.4178, 0.6270],
        [0.3862, 0.5342],
        [0.4315, 0.5537],
        [0.3670, 0.5444],
        [0.3283, 0.3935],
        [0.3949, 0.5342],
        [0.3681, 0.5429],
        [0.3679, 0.5590],
        [0.4154, 0.6273],
        [0.4467, 0.5785],
        [0.3706, 0.5619],
        [0.3683, 0.5595],
        [0.4466, 0.5848],
        [0.3655, 0.5468],
        [0.3916, 0.5339],
        [0.3809, 0.5348],
        [0.3835, 0.5556],
        [0.3752, 0.5633],
        [0.4465, 0.5860],
        [0.3710, 0.5622],
        [0.4467, 0.5778],
        [0.4253, 0.5479]], grad_fn=<SigmoidBackward>)
In [101]:
# Parameters
n_batch = 100
epochs = 100

half_batch = int(n_batch / 2)
labels = ['real', 'fake']
d_X = {}
d_y = {}
d_accuracy = {}
model, criterion, optimizer = define_discriminator()
# run epochs manually
for i in range(epochs):
    
    for label in labels:
        print(f'processing label: {label}')
        # generate real examples
        d_X[label], d_y[label] = generate_samples(half_batch, label=label)
#         update model
#         model.train_on_batch(d_data[label])
        # zero the parameter gradients
        optimizer.zero_grad()
        # forward + backward + optimize
        outputs = model(torch.Tensor(d_X[label]))
        _, predicted = torch.max(outputs, 1)

#         pred = torch.sigmoid(outputs)
        _, pred = torch.max(outputs, 1)
        loss = F.binary_cross_entropy(pred.view(-1, 1), torch.Tensor(d_y[label]))

#         loss = criterion(predicted.view(-1, 1).double(), torch.Tensor(d_y[label]).view(-1, 1).double())
        loss.backward()
        optimizer.step()

        # print statistics
        running_loss += loss.item()
        
#         # generate fake examples
#         X_fake, y_fake = generate_samples(half_batch, label='fake')
#         # update model
#         model.train_on_batch(X_fake, y_fake)
#         # evaluate the model
#         _, acc_real = model.evaluate(X_real, y_real, verbose=0)
#         _, acc_fake = model.evaluate(X_fake, y_fake, verbose=0)
#     print(i, acc_real, acc_fake)
processing label: real
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-101-2e0f70b78835> in <module>
     26 #         pred = torch.sigmoid(outputs)
     27         _, pred = torch.max(outputs, 1)
---> 28         loss = F.binary_cross_entropy(pred.view(-1, 1), torch.Tensor(d_y[label]))
     29 
     30 #         loss = criterion(predicted.view(-1, 1).double(), torch.Tensor(d_y[label]).view(-1, 1).double())

~/anaconda3/envs/py367/lib/python3.6/site-packages/torch/nn/functional.py in binary_cross_entropy(input, target, weight, size_average, reduce, reduction)
   2025 
   2026     return torch._C._nn.binary_cross_entropy(
-> 2027         input, target, weight, reduction_enum)
   2028 
   2029 

RuntimeError: _thnn_binary_cross_entropy_forward is not implemented for type torch.LongTensor
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]: