【笔记】第四章:从线性回归到逻辑回归-Mastering Machine Learning With scikit-learn
(这章起换用Markdown)
《Mastering Machine Learning With scikit-learn》是一本非常实用的机器学习实战书籍,它使用通俗易懂的语言和适度的理论知识来帮助python开发者使用scikit-learn机器学习工具包实现机器学习相关算法的应用。早些时候我拜读了这本书,记了些笔记和重要的知识点,今天重新温习了遍,仍有收获,顺便张贴到博客上,一起学习交流。
第四章:从线性回归到逻辑回归
广义线性回归是一种灵活的框架,比普通线性回归要求更少的假设。这一章,我们讨论广义线性回归模型的具体形式的另一种形式,逻辑回归(logistic regression)。
和前面讨论的模型不同,逻辑回归是用来做分类任务的。
分类任务的目标是找一个函数,把观测值匹配到相关的类和标签上。学习算法必须用成对的特征向量和对应的标签来估计匹配函数的参数,从而实现更好的分类效果。
在二元分类(binary classification)中,分类算法必须把一个实例配置两个类别。多元分类中,分类算法需要为每个实例都分类一组标签。
逻辑回归处理二元分类
普通的线性回归假设响应变量呈正态分布,也称为 高斯分布(Gaussian distribution)或 钟形曲线(bell curve)。正态分布数据是对称的,且均值,中位数和众数(mode)是一样的。
很多自然现象都服从正态分布。比如,人类的身高就服从正态分布,姚明那样的高度极少,在99%之外了。 在某些问题里,响应变量不是正态分布的。比如,掷一个硬币获取正反两面的概率分布是 伯努利分布(Bernoulli distribution),又称两点分布或者0-1分布。表示一个事件发生的概率是P,不发生的概率是1-P,概率在{0,1}之间。 线性回归假设解释变量值的变化会引起响应变量值的变化,如果响应变量的值是概率的,这条假设就不满足了。广义线性回归去掉了这条假设,用一个 联连函数(link function)来描述解释变量与响应变量的关系。
在逻辑回归里,响应变量描述了类似于掷一个硬币结果为正面的概率。如果响应变量等于或超过了指定的临界值,预测结果就是正面,否则预测结果就是反面。响应变量是一个像线性回归中的解释变量构成的函数表示,称为 逻辑函数(logistic function)。
一个值在{0,1}之间的逻辑函数如下所示:
$$F\left(t\right)=\frac{1}{1+e^{-t}}$$
下面是在{-6,6}的图形:
%matplotlib inline
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\msyh.ttc", size=10)
import numpy as np
plt.figure()
plt.axis([-6, 6, 0, 1])
plt.grid(True)
X = np.arange(-6,6,0.1)
y = 1 / (1 + np.e ** (-X))
plt.plot(X, y, 'b-')
在逻辑回归中, 是解释变量的线性组合,公式如下:
$$F\left(t\right)=\frac{1}{1+e^{-(\beta_{0}+\beta_{x})}}$$
对数函数(logit function)是逻辑函数的逆运算:
$$g\left(x\right)=\ln\frac{F\left(x\right)}{1-F\left(x\right)}=\beta_{0}+\beta_{x}$$
垃圾邮件分类
经典的二元分类问题就是 垃圾邮件分类(spam classification)。 这里,我们分类垃圾短信。我们用第三章介绍的TF-IDF算法来抽取短信的特征向量,然后用逻辑回归分类。
首先,我们还是用Pandas做一些描述性统计:
import pandas as pd
df = pd.read_csv('mlslpic/SMSSpamCollection', delimiter='\t', header=None
) print(df.head())
print('含spam短信数量:', df[df[0] == 'spam'][0].count())
print('含ham短信数量:', df[df[0] == 'ham'][0].count())
# 0 1
# 0 ham Go until jurong point, crazy.. Available only ...
# 1 ham Ok lar... Joking wif u oni...
# 2 spam Free entry in 2 a wkly comp to win FA Cup fina...
# 3 ham U dun say so early hor... U c already then say...
# 4 ham Nah I don't think he goes to usf, he lives aro...
# 含spam短信数量: 7
# 含ham短信数量: 4825
每条信息的前面已经被打上了标签。共5574条短信里面,4827条是ham,747条是spam。ham短信用0标记,spam短信用1标记。观察数据会看到更多建模时需要的信息。下面的几条信息体现两种类型的特征:
Spam: Free entry in 2 a wkly comp to win FA Cup final tkts 21st May
Text FA to 87121 to receive entry question(std txt rate)T&C's apply 08452810075over18's
Spam: WINNER!! As a valued network customer you have been selected to receivea £900
prize reward! To claim call 09061701461. Claim code KL341. Valid 12 hours only.
Ham: Sorry my roommates took forever, it ok if I come by now?
Ham: Finished class where are you.
让我们用 LogisticRegression
类来预测:
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model.logistic import LogisticRegression
from sklearn.cross_validation import train_test_split
首先,用 pandas
加载数据.csv文件,然后用 train_test_split
分成训练集(75%)和测试集(25%):
df = pd.read_csv('mlslpic/SMSSpamCollection', delimiter='\t', header=None)
X_train_raw, X_test_raw, y_train, y_test =train_test_split(df[1],df[0])
然后,我们建一个 TfidfVectorizer
实例来计算TF-IDF权重:
vectorizer = TfidfVectorizer()
X_train = vectorizer.fit_transform(X_train_raw)
X_test = vectorizer.transform(X_test_raw)
最后,我们建一个 LogisticRegression
实例来训练模型。和 LinearRegression
类似, LogisticRegression
同样实现 fit()
和 predict()
方法。最后把结果打印出来看看:
classifier = LogisticRegression()
classifier.fit(X_train, y_train)
predictions = classifier.predict(X_test)
for i, prediction in enumerate(predictions[-5:]):
print('预测类型:%s. 信息:%s' % (prediction, X_test_raw.iloc[i]))
# 预测类型:ham. 信息:Are u coming to the funeral home
# 预测类型:ham. 信息:Love isn't a decision, it's a feeling. If we could decide who to love, then, life would be much simpler, but then less magical
# 预测类型:ham. 信息:Dont think so. It turns off like randomlly within 5min of opening
# 预测类型:spam. 信息:Hey happy birthday...
# 预测类型:ham. 信息:None of that's happening til you get here though
二元分类效果评估方法
二元分类的效果评估方法有很多,常见的包括第一章里介绍的肿瘤预测使用的 准确率(accuracy), 精确率(precision)和 召回率(recall)三项指标,以及 综合评价指标(F1 measure), ROC,AUC值(Receiver Operating Characteristic ROC,Area Under Curve,AUC)。这些指标评价的样本分类是 真阳性(true positives), 真阴性(true negatives), 假阳性(false positives), 假阴性(false negatives)。阳性和阴性指分类,真和假指预测的正确与否。
混淆矩阵(Confusion matrix),也称 列联表分析(Contingency table)可以用来描述真假与阴阳的关系。矩阵的行表示实际类型,列表示预测类型。
from sklearn.metrics import confusion_matrix #引入混淆矩阵
import matplotlib.pyplot as plt
y_test = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
y_pred = [0, 1, 0, 0, 0, 0, 0, 1, 1, 1]
confusion_matrix = confusion_matrix(y_test, y_pred)
print(confusion_matrix)
plt.matshow(confusion_matrix)
plt.title('混淆矩阵',fontproperties=font)
plt.colorbar()
plt.ylabel('实际类型',fontproperties=font)
plt.xlabel('预测类型',fontproperties=font)
plt.show()
# [[4 1]
# [2 3]]
准确率
准确率是分类器预测正确性的评估指标。 scikit-learn
提供了 accuracy_score
来计算:
from sklearn.metrics import accuracy_score
y_pred, y_true = [0, 1, 1, 0], [1, 1, 1, 1]
print(accuracy_score(y_true, y_pred))
# 0.5
LogisticRegression.score()
用来计算模型预测的准确率:
import numpy as np
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model.logistic import LogisticRegression
from sklearn.cross_validation import train_test_split, cross_val_score
df = pd.read_csv('mlslpic/sms.csv')
X_train_raw, X_test_raw, y_train, y_test = train_test_split(df['message'], df['label'])
vectorizer = TfidfVectorizer()
X_train = vectorizer.fit_transform(X_train_raw)
X_test = vectorizer.transform(X_test_raw)
classifier = LogisticRegression()
classifier.fit(X_train, y_train)
scores = cross_val_score(classifier, X_train, y_train, cv=5)
print('准确率:',np.mean(scores), scores)
# 准确率: 0.958373205742 [ 0.96291866 0.95334928 0.95813397 0.96172249 0.95574163]
准确率是分类器预测正确性的比例,但是并不能分辨出假阳性错误和假阴性错误。
有时准确率并非一个有效的衡量指标,如果分类的比例在样本中严重失调。
精确率和召回率
在本章的垃圾短信分类器中,精确率是指分类器预测出的垃圾短信中真的是垃圾短信的比例:
$$ P=\frac{TP}{TP+FP} $$
召回率在医学领域也叫做 灵敏度(sensitivity),在本例中是指所有真的垃圾短信被分类器正确找出来的比例。
$$ R=\frac{TP}{TP+FN} $$
精确率和召回率各自含有的信息都很少,它们对分类器效果的观察角度不同。精确率和召回率都不能从表现差的一种分类器中区分出好的分类器。
scikit-learn
结合真实类型数据,提供了一个函数来计算一组预测值的精确率和召回率。
import numpy as np
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model.logistic import LogisticRegression
from sklearn.cross_validation import train_test_split, cross_val_score
df = pd.read_csv('mlslpic/sms.csv')
X_train_raw, X_test_raw, y_train, y_test = train_test_split(df['message']
, df['label'])
vectorizer = TfidfVectorizer()
X_train = vectorizer.fit_transform(X_train_raw)
X_test = vectorizer.transform(X_test_raw)
classifier = LogisticRegression()
classifier.fit(X_train, y_train)
precisions = cross_val_score(classifier, X_train, y_train, cv=5, scoring='precision')
print('精确率:', np.mean(precisions), precisions)
recalls = cross_val_score(classifier, X_train, y_train, cv=5, scoring='recall')
print('召回率:', np.mean(recalls), recalls)
# 精确率: 0.99217372134 [ 0.9875 0.98571429 1. 1. 0.98765432]
# 召回率: 0.672121212121 [ 0.71171171 0.62162162 0.66363636 0.63636364 0.72727273]
我们的分类器精确率99.2%,分类器预测出的垃圾短信中99.2%都是真的垃圾短信。召回率比较低67.2%,就是说真实的垃圾短信中,32.8%被当作正常短信了,没有被识别出来。这些数据会不断变化,因为训练集和测试集是随机抽取的。
计算综合评价指标
综合评价指标(F1 measure)是精确率和召回率的 调和均值(harmonic mean),或加权平均值,也称为 F-measure或 F-score。
$$ \frac{1}{F1}+\frac{1}{F1}=\frac{1}{P}+\frac{1}{R} $$
$$ F1=2\frac{PR}{P+R} $$
综合评价指标平衡了精确率和召回率。一个二元分类模型,精确率和召回率为1,那么综合评价指标为1。如果精确率或召回率为0,那么综合评价指标为0。 scikit-learn
也提供了计算综合评价指标的函数。
f1s = cross_val_score(classifier, X_train, y_train, cv=5, scoring='f1')
print('综合评价指标:', np.mean(f1s), f1s)
# 综合评价指标: 0.800588878125 [ 0.82722513 0.76243094 0.7978142 1 0.77777778 0.83769634]
本例的综合评价指标是80%。由于精确率和召回率的差异比较小,所以综合评价指标的罚值也比较小。有时也会用F0.5和F2,表示精确率权重大于召回率,或召回率权重大于精确率。
ROC AUC
ROC曲线(Receiver Operating Characteristic,ROC curve)可以用来可视化分类器的效果。和准确率不同,ROC曲线对分类比例不平衡的数据集不敏感,ROC曲线显示的是对超过限定阈值的所有预测结果的分类器效果。ROC曲线画的是分类器的召回率与误警率(fall-out)的曲线。误警率也称假阳性率,是所有阴性样本中分类器识别为阳性的样本所占比例:
$$ F=\frac{FP}{TN+FP} $$
AUC是ROC曲线下方的面积,它把ROC曲线变成一个值,表示分类器随机预测的效果。 scikit-learn
提供了计算ROC和AUC指标的函数:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model.logistic import LogisticRegression
from sklearn.cross_validation import train_test_split, cross_val_score
from sklearn.metrics import roc_curve, auc
df = pd.read_csv('mlslpic/sms.csv')
X_train_raw, X_test_raw, y_train, y_test = train_test_split(df['message'], df['label'])
vectorizer = TfidfVectorizer()
X_train = vectorizer.fit_transform(X_train_raw)
X_test = vectorizer.transform(X_test_raw)
classifier = LogisticRegression()
classifier.fit(X_train, y_train)
predictions = classifier.predict_proba(X_test) #输出的是概率
false_positive_rate, recall, thresholds = roc_curve(y_test, predictions[:, 1])
roc_auc = auc(false_positive_rate, recall)
plt.title('Receiver Operating Characteristic')
plt.plot(false_positive_rate, recall, 'b', label='AUC = %0.2f' % roc_auc)
plt.legend(loc='lower right')
plt.plot([0, 1], [0, 1], 'r--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.0])
plt.ylabel('Recall')
plt.xlabel('Fall-out')
plt.show()
网格搜索
在第二章我们曾经提到过超参数,是需要手动调节的参数,模型无法学习。比如,在我们的垃圾短信分类模型中,超参数出现在TF-IDF中,用来移除太频繁和太稀缺单词的频率阈值,目前函数正则化的权重值。
在 scikit-learn
里面,超参数是在模型建立时设置的。
网格搜索(Grid search)就是用来确定最优超参数的方法。其原理就是选取可能的参数不断运行模型获取最佳效果。网格搜索用的是穷举法,其缺点在于即使每个超参数的取值范围都很小,计算量也是巨大的。不过这是一个并行问题,参数与参数彼此独立,计算过程不需要同步,所有很多方法都可以解决这个问题。
scikit-learn
有 GridSearchCV()
函数解决这个问题:
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model.logistic import LogisticRegression
from sklearn.grid_search import GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.cross_validation import train_test_split
from sklearn.metrics import precision_score, recall_score, accuracy_score
pipeline = Pipeline([
('vect', TfidfVectorizer(stop_words='english')),
('clf', LogisticRegression())
])
parameters = {
'vect__max_df': (0.25, 0.5, 0.75),
'vect__stop_words': ('english', None),
'vect__max_features': (2500, 5000, 10000, None),
'vect__ngram_range': ((1, 1), (1, 2)),
'vect__use_idf': (True, False),
'vect__norm': ('l1', 'l2'),
'clf__penalty': ('l1', 'l2'),
'clf__C': (0.01, 0.1, 1, 10),
} grid_search =
GridSearchCV(pipeline, parameters, n_jobs=-1, verbose=1, sc
oring='accuracy', cv=3)
df = pd.read_csv('mlslpic/sms.csv')
X, y, = df['message'], df['label']
X_train, X_test, y_train, y_test = train_test_split(X, y)
grid_search.fit(X_train, y_train)
print('最佳效果:%0.3f' % grid_search.best_score_)
print('最优参数组合:')
best_parameters = grid_search.best_estimator_.get_params()
for param_name in sorted(parameters.keys()):
print('\t%s: %r' % (param_name, best_parameters[param_name]))
predictions = grid_search.predict(X_test)
print('准确率:', accuracy_score(y_test, predictions))
print('精确率:', precision_score(y_test, predictions))
print('召回率:', recall_score(y_test, predictions))
# [Parallel(n_jobs=-1)]: Done 1 jobs | elapsed: 1.8s
# [Parallel(n_jobs=-1)]: Done 50 jobs | elapsed: 10.1s
# [Parallel(n_jobs=-1)]: Done 200 jobs | elapsed: 27.4s
# [Parallel(n_jobs=-1)]: Done 450 jobs | elapsed: 54.2s
# [Parallel(n_jobs=-1)]: Done 800 jobs | elapsed: 1.6min
# [Parallel(n_jobs=-1)]: Done 1250 jobs | elapsed: 2.4min
# [Parallel(n_jobs=-1)]: Done 1800 jobs | elapsed: 3.4min
# [Parallel(n_jobs=-1)]: Done 2450 jobs | elapsed: 4.6min
# [Parallel(n_jobs=-1)]: Done 3200 jobs | elapsed: 6.0min
# [Parallel(n_jobs=-1)]: Done 4050 jobs | elapsed: 10.6min
# [Parallel(n_jobs=-1)]: Done 4608 out of 4608 | elapsed: 11.7min
# finished
# Fitting 3 folds for each of 1536 candidates, totalling 4608 fits
# 最佳效果:0.982
# 最优参数组合:
# clf__C: 10
# clf__penalty: 'l2'
# vect__max_df: 0.25
# vect__max_features: 2500
# vect__ngram_range: (1, 2)
# vect__norm: 'l2'
# vect__stop_words: None
# vect__use_idf: True
# 准确率: 0.989956958393
# 精确率: 0.994252873563
# 召回率: 0.930107526882
GridSearchCV()
函数的参数有待评估模型 pipeline
,超参数词典 parameters
和效果评价指标 scoring
。 n_jobs
是指并发进程最大数量,设置为-1表示使用所有CPU核心进程。在Python3.4中,可以写一个Python的脚本,让 fit()
函数可以在 main()
函数里调用,也可以在Python自带命令行,IPython命令行和IPython Notebook运行。经过网格计算后的超参数在训练集中取得了很好的效果。
多类分类
现实中有很多问题不只是分成两类,许多问题都需要分成多个类,成为 多类分类问题(Multi-class classification)。
scikit-learn
用one-vs.-all或one-vs.-the-rest方法实现多类分类,就是把多类中的每个类都作为二元分类处理。分类器预测样本不同类型,将具有最大置信水平的类型作为样本类型。
LogisticRegression()
通过one-vs.-all策略支持多类分类。
我们利用烂番茄(Rotten Tomatoes)网站影评短语数据对电影进行评价。
每个影评可以归入下面5个类项:不给力(negative),不太给力(somewhat negative),中等(neutral),有点给力(somewhat positive), 给力(positive)。
import zipfile
# 压缩节省空间
z = zipfile.ZipFile('mlslpic/train.zip')
df = pd.read_csv(z.open(z.namelist()[0]), header=0, delimiter='\t')
df.head()
df.count()
# PhraseId 156060
# SentenceId 156060
# Phrase 156060
# Sentiment 156060
# dtype: int64
df.Phrase.head(10)
# 0 A series of escapades demonstrating the adage ...
# 1 A series of escapades demonstrating the adage ...
# 2 A series
# 3 A
# 4 series
# 5 of escapades demonstrating the adage that what...
# 6 of
# 7 escapades demonstrating the adage that what is...
# 8 escapades
# 9 demonstrating the adage that what is good for ...
# Name: Phrase, dtype: object
df.Sentiment.describe()
# count 156060.000000
# mean 2.063578
# std 0.893832
# min 0.000000
# 25% 2.000000
# 50% 2.000000
# 75% 3.000000
# max 4.000000
# Name: Sentiment, dtype: float64
df.Sentiment.value_counts()
# 2 79582
# 3 32927
# 1 27273
# 4 9206
# 0 7072
# dtype: int64
df.Sentiment.value_counts()/df.Sentiment.count()
# 2 0.509945
# 3 0.210989
# 1 0.174760
# 4 0.058990
# 0 0.045316
# dtype: float64
用 scikit-learn
来训练分类器:
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model.logistic import LogisticRegression
from sklearn.cross_validation import train_test_split
from sklearn.metrics import classification_report, accuracy_score, confusion_matrix
from sklearn.pipeline import Pipeline
from sklearn.grid_search import GridSearchCV
import zipfile
pipeline = Pipeline([
('vect', TfidfVectorizer(stop_words='english')),
('clf', LogisticRegression())
])
parameters = {
'vect__max_df': (0.25, 0.5),
'vect__ngram_range': ((1, 1), (1, 2)),
'vect__use_idf': (True, False),
'clf__C': (0.1, 1, 10),
}
z = zipfile.ZipFile('mlslpic/train.zip')
df = pd.read_csv(z.open(z.namelist()[0]), header=0, delimiter='\t')
X, y = df['Phrase'], df['Sentiment'].as_matrix()
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.5)
grid_search = GridSearchCV(pipeline, parameters, n_jobs=3, verbose=1, scoring='accuracy')
grid_search.fit(X_train, y_train)
print('最佳效果:%0.3f' % grid_search.best_score_)
print('最优参数组合:')
best_parameters = grid_search.best_estimator_.get_params()
for param_name in sorted(parameters.keys()):
print('\t%s: %r' % (param_name, best_parameters[param_name]))
多类分类效果评估
二元分类里,混淆矩阵可以用来可视化不同分类错误的数据。每种类型的精确率,召回率和综合评价指标(F1 score)可以计算,所有预测的准确率也可以计算。
predictions = grid_search.predict(X_test)
print('准确率:', accuracy_score(y_test, predictions))
print('混淆矩阵:', confusion_matrix(y_test, predictions))
print('分类报告:', classification_report(y_test, predictions))
# 准确率: 0.635024990388
# 混淆矩阵: [[ 1178 1701 616 71 4]
# [ 990 5993 6030 563 30]
# [ 227 3231 32668 3520 143]
# [ 37 401 6642 8089 1305]
# [ 7 30 534 2397 1623]]
# 分类报告: precision recall f1-score support
# 0 0.48 0.33 0.39 3570
# 1 0.53 0.44 0.48 13606
# 2 0.70 0.82 0.76 39789
# 3 0.55 0.49 0.52 16474
# 4 0.52 0.35 0.42 4591
# avg / total 0.62 0.64 0.62 78030
我们通过网格搜索获得了最佳参数组合,最终的分类器是通过对开始的分类器不断优化得到的。
多标签分类和问题转换
前面我们讨论了二元分类,多类分类,还有一种分类问题是 多标签分类(multi-label classification)。
每个样本可以拥有全部类型的一部分类型。这样的例子太普遍了,比如统计班上同学一周7天里哪天有空。每个同学都会在周一到周日这7天里,根据自己的情况分别打勾。再比如常见的博客文章分类标签,一篇文章一般都有好几个标签等等。
多标签分类问题一般有两种解决方法。
问题转化方法(Problem transformation)可以将多标签问题转化成单标签问题。 第一种转换方法是训练集里面每个样本通过幂运算转换成单标签。比如下面数据里面每篇文章都带有若干标签。
转换方法就是用幂运算将多个类合并成一个类,比如样本1有Local和US类,新建一个标签为Local^US类,这样多标签就变成单标签了。
这样原来5个标签现在变成了7个标签。这种幂运算虽然直观,但是并不实用,因为这样做多出来的标签只有一小部分样本会用到。而且,这些标签只能在训练集里面学习这些类似,在测试集中依然无法使用。
另外一种问题转换方法就是每个标签都用二元分类处理。每个标签的分类器都预测样本是否属于该标签。我们的例子中需要5个二元分类器,第一个分类器预测样本是否应该被归入Local类,第二个分类器预测样本是否应该被归入US类,以此类推。预测最后一步就是把这些分类结果求并集,如下图所示:
这个问题确保了单标签问题和多标签问题有同样的训练集,只是忽略了标签之间的关联关系。
多标签分类效果评估
多标签分类效果评估最常用的手段是 汉明损失函数(Hamming loss)和 杰卡德相似度(Jaccard similarity)。
汉明损失函数表示错误标签的平均比例,是一个函数,当预测全部正确,即没有错误标签时,值为0。
杰卡德相似度或杰卡德相指数(Jaccard index),是预测标签和真实标签的交集数量除以预测标签和真实标签的并集数量。其值在{0,1}之间,公式如下:
$$ J\left(Predicted, True\right)=\frac{|Predicted \bigcap True|}{|Predicted \bigcup True|} $$
import numpy as np
from sklearn.metrics import hamming_loss, jaccard_similarity_score
print(hamming_loss(np.array([[0.0, 1.0], [1.0, 1.0]]), np.array([[0.0, 1.0], [1.0, 1.0]])))
print(hamming_loss(np.array([[0.0, 1.0], [1.0, 1.0]]), np.array([[1.0, 1.0], [1.0, 1.0]])))
print(hamming_loss(np.array([[0.0, 1.0], [1.0, 1.0]]), np.array([[1.0, 1.0], [0.0, 1.0]])))
print(jaccard_similarity_score(np.array([[0.0, 1.0], [1.0, 1.0]]), np.array([[0.0, 1.0], [1.0, 1.0]])))
print(jaccard_similarity_score(np.array([[0.0, 1.0], [1.0, 1.0]]), np.array([[1.0, 1.0], [1.0, 1.0]])))
print(jaccard_similarity_score(np.array([[0.0, 1.0], [1.0, 1.0]]), np.array([[1.0, 1.0], [0.0, 1.0]]))
# 0.0
# 0.25
# 0.5
# 1.0
# 0.75
# 0.