《蜥蜴书》_讲义及源码解读_09

无监督学习


绝大多数可用数据都是没有标签的。计算机科学家 Yann LeCun 杨立昆曾有句名言:“如果智能是蛋糕,无监督学习将是蛋糕的本体,有监督学习是蛋糕上的糖霜,强化学习是蛋糕上的樱桃”,换句话说,无监督学习具有巨大的潜力,才刚开始起步。

在这里插入图片描述

最常见的无监督学习任务就是前面介绍的降维。

聚类Clustering

识别相似实例,并将其分配给相似实例的集群或组

在这里插入图片描述

如图左,标记类别的鸢尾花数据,非常适合使用各种分类算法。如逻辑回归,随机森林,SVM

如图右,没有标签标记的相同的数据集,不能再使用分类算法。引入聚类。

图右的左下侧数据很容易检测到,但右上侧数据是否能分成两个集群(组),并不明显、并不确定。

如图,只是用了鸢尾花数据集的两个特征(花瓣长宽),如果使用所有特征呢,能很好的识别这个三聚类

聚类取决于上下文,不同算法会得到不同的聚类结果(不同的集群)

聚类的应用领域:
  • 客户细分
  • 数据分析
  • 降维技术
  • 异常检测
  • 半监督学习
  • 搜索引擎
  • 分割图像

两种流行的聚类算法:K-MeansDBSCAN

K-Means算法

指定类别参数 K ,尝试找到 K 个集群中每个集群的中心点,并将每个实例分配给最近的集群。

示例代码:使用make_blobs生成数据
from sklearn.datasets import make_blobs  #生成用于聚类训练的数据集

blob_centers = np.array(    # 五个聚类的中心点位置
    [[ 0.2,  2.3],
     [-1.5 ,  2.3],
     [-2.8,  1.8],
     [-2.8,  2.8],
     [-2.8,  1.3]])
blob_std = np.array([0.4, 0.3, 0.1, 0.1, 0.1])  # 每个聚类的方差

X, y = make_blobs(n_samples=2000, centers=blob_centers,
                  cluster_std=blob_std, random_state=7)  

# n_samples=2000 样本个数,生成2000个样本实例,
# n_features,每个样本实例的特征个数,默认:2,
# centers ,聚类中心点
# 每个聚类,每个类别的方差。

#描图
def plot_clusters(X, y=None):
    plt.scatter(X[:, 0], X[:, 1], c=y, s=1)
    plt.xlabel("$x_1$", fontsize=14)
    plt.ylabel("$x_2$", fontsize=14, rotation=0)
plt.figure(figsize=(8, 4))
plot_clusters(X)
plt.show()

在这里插入图片描述

示例代码:训练拟合和预测
from sklearn.cluster import KMeans
k = 5
kmeans = KMeans(n_clusters=k, random_state=42)
y_pred = kmeans.fit_predict(X)

kmeans.cluster_centers_
#array([[-2.80389616,  1.80117999],
#       [ 0.20876306,  2.25551336],
#       [-2.79290307,  2.79641063],
#       [-1.46679593,  2.28585348],
#       [-2.80037642,  1.30082566]])

在这里插入图片描述

如图,少数实例(左上角)贴错了标签。当集群具有不同的直径时,K-Means 算法的性能不是很好。只关心距离中心点的距离。

硬聚类和软聚类:

把每个实例分配给一个单独的集群(组),称硬聚类

为每个实例分配一个集群的相似性分数,这个分数可以是距离,也可是是高斯径向函数GBF软聚类

X_new = np.array([[0, 2], [3, 2], [-3, 3], [-3, 2.5]]) 
kmeans.predict(X_new) 
# 4个测试点属于聚类:1,1,2,3:array([1, 1, 2, 2])  ,硬聚类
kmeans.transform(X_new)
# 直接给出分数(距离五个中心点的距离), 软聚类
array([[2.81093633, 0.32995317, 2.9042344 , 1.49439034, 2.88633901],
       [5.80730058, 2.80290755, 5.84739223, 4.4759332 , 5.84236351],
       [1.21475352, 3.29399768, 0.29040966, 1.69136631, 1.71086031],
       [0.72581411, 3.21806371, 0.36159148, 1.54808703, 1.21567622]])
K-Means算法基本思想
  • 在只有参数K、没有中心点、也没有标签的情况下,只能先随机选择K个中心点(如下图上左)。

  • 然后,根据K 个中心点标记实例(如下图上右),根据实例更新中心点(如下图中左),再标记实例(如下图中右)

  • 然后,再更新中心点(如下图下左),有限数量的迭代后必收敛(中心点停止移动),不会永远振荡。

  • 该算法计算复杂度在实例数量 m,集群数 K,维度 n 方面是线性的。仅当数据具有聚类结构时才如此,否则,在最坏的情况下,复杂度会随着实例数量的增加而呈指数增长。但这种情况很少发生。

K-Means通常是最快的聚类算法之一。

在这里插入图片描述

中心点初始化方法

法1: 碰巧知道或通过其他聚类算法得到了中心点。即已知中心点,可以直接设置超参数init ,并将n_init=1

from sklearn.cluster import KMeans
good_init=np.array([[-3,3],[-3,2],[-3,1],[-1,2],[0,2]])
# 已知中心点
KMeans(n_clusters=5, init=good_init, n_init=1, random_state=5)
# init=? 中心点初始值的选择方式:'random' 或 ‘k-means++‘或直接指定,如本例
# n_init= ? 用不同的初始化中心点运行算法的次数,默认为10

法2:使用不同的随机初始化多次运行算法,并保留最优解。。用每个实例与其最接近的中心点之间的均方距离作为衡量n个随机初始化的性能指标。这个指标也称模型的惯性

from sklearn.cluster import KMeans
def plot_clusterer_comparison(clusterer1, clusterer2, X, title1=None, title2=None):
    clusterer1.fit(X)
    clusterer2.fit(X)

    plt.figure(figsize=(10, 3.2))

    plt.subplot(121)
    plot_decision_boundaries(clusterer1, X)
    if title1:
        plt.title(title1, fontsize=14)

    plt.subplot(122)
    plot_decision_boundaries(clusterer2, X, show_ylabels=False)
    if title2:
        plt.title(title2, fontsize=14)

        
kmeans_rnd_init1 = KMeans(n_clusters=5, init="random", n_init=1,
                         algorithm="full", random_state=2)
kmeans_rnd_init2 = KMeans(n_clusters=5, init="random", n_init=1,
                         algorithm="full", random_state=5)  
# algorithm:有"auto", “full","elkan"三种选择,数据稠密选"elkan",数据稀疏选“full"
# “full"指强制使用原始K-Means算法,"elkan"指elkan K-Means算法

plot_clusterer_comparison(kmeans_rnd_init1, kmeans_rnd_init2, X,
                          "Solution 1", "Solution 2 (with a different random init)")
plt.show()

在这里插入图片描述

寻找最佳聚类数

法1:绘制惯性相对于集群数量 K 的函数曲线,肘(Elbow)的位置,即不错的K的选择。如图
在这里插入图片描述

法2:使用轮廓分数。它是所有实例的平均轮廓系数。实例的轮廓系数等于:
( b − a ) / m a x ( a , b ) a : 与 同 一 集 群 中 其 他 实 例 的 平 均 距 离 ( 集 群 内 平 均 距 离 ) b : 平 均 最 近 集 群 距 离 ( 到 下 一 个 最 近 集 群 实 例 的 平 均 距 离 ) 该 系 数 ( − 1 , + 1 ) 之 间 : 接 近 + 1 , 说 明 该 实 例 很 好 的 位 于 自 身 集 群 中 并 且 远 离 其 他 集 群 ; 0 附 近 说 明 该 实 例 接 近 一 个 集 群 的 边 界 − 1 附 近 , 说 明 该 实 例 可 能 已 分 配 到 错 误 的 集 群 (b-a)/max(a,b) \\ a: 与同一集群中其他实例的平均距离(集群内平均距离)\\ b: 平均最近集群距离(到下一个最近集群实例的平均距离)\\ 该系数(-1,+1)之间:\\ 接近+1,说明该实例很好的位于自身集群中并且远离其他集群;\\ 0附近说明该实例接近一个集群的边界\\ -1附近,说明该实例可能已分配到错误的集群 (ba)/max(a,b)a:b:(1,+1)+101
比较不同集群数量K的轮廓分数。如图:

在这里插入图片描述

K-Means的局限

尽管K-Means有很多优点,尤其是快速且可扩展,但并不完美。

  • 必须多次运行算法才能避免次优解;
  • 必须指定集群数K,这很麻烦
  • 当集群具有不同大小不同密度非球形时,K-Means表现也不佳。
  • 还要注意,运行K-Means算法之前,要先缩放输入特征,否则集群可能会变形,K-Means性能会很差。
  • 总之,要根据数据,用不同的聚类算法。
使用聚类进行图像分割
import urllib.request
images_path = os.path.join(PROJECT_ROOT_DIR, "images", "unsupervised_learning")
os.makedirs(images_path, exist_ok=True)
DOWNLOAD_ROOT = "https://raw.githubusercontent.com/ageron/handson-ml2/master/"
filename = "ladybug.png"
print("Downloading", filename)
url = DOWNLOAD_ROOT + "images/unsupervised_learning/" + filename
urllib.request.urlretrieve(url, os.path.join(images_path, filename))
#下载图像
from matplotlib.image import imread
image = imread(os.path.join(images_path, filename))
image.shape
#读取图像 (533, 800, 3) (高度,宽度,颜色通道)

segmented_imgs = []
n_colors = (10, 8, 6, 4, 2)
# 对颜色通道(3维特征),10聚类,8聚类,……
for n_clusters in n_colors:
    kmeans = KMeans(n_clusters=n_clusters, random_state=42).fit(X)
    segmented_img = kmeans.cluster_centers_[kmeans.labels_] 
    # n_colors颜色数量作为聚类数量,cluster_centers_聚类后每类的中心点(可以简单理解为平均颜色)
    segmented_imgs.append(segmented_img.reshape(image.shape))
    # 重构5个图形(高,宽,通道)

 
plt.figure(figsize=(10,5))
plt.subplots_adjust(wspace=0.05, hspace=0.1)

plt.subplot(231)
plt.imshow(image)
plt.title("Original image")
plt.axis('off')

#每个idx
for idx, n_clusters in enumerate(n_colors):
    plt.subplot(232 + idx)
    plt.imshow(segmented_imgs[idx])
    plt.title("{} colors".format(n_clusters))
    plt.axis('off')
plt.show()

在这里插入图片描述

使用聚类进行预处理

使用digits数据集(简版的,类似MNIST的数据集),包含1797个代表数字0到9的灰度8×8图像。

from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

X_digits, y_digits = load_digits(return_X_y=True)
X_digits, y_digits = load_digits(return_X_y=True)
X_digits.shape ,y_digits.shape  #((1797, 64), (1797,))

X_train, X_test, y_train, y_test = train_test_split(X_digits, y_digits, random_state=42)
X_train.shape,X_test.shape 
# (1347, 64), (450, 64) 切分训练和测试集

log_reg = LogisticRegression(multi_class="ovr", solver="lbfgs", max_iter=5000, random_state=42)  
# ovr : 1对剩余的2分类;solver="lbfgs" 拟牛顿法的一种,利用损失函数二阶导数矩阵即海森矩阵来迭代优化损失函数
log_reg.fit(X_train, y_train)

log_reg_score = log_reg.score(X_test, y_test)
log_reg_score  #0.9688888888888889
#评估

96.89%的准确率,BASELINE~

让我们看看使用K-Means作为预处理步骤是否可以做得更好。

创建一管道,首先将训练集分为50个集群,并用图像到50个集群的距离替换图像,然后应用逻辑回归模型:

from sklearn.pipeline import Pipeline
from sklearn.cluster import KMeans
pipeline = Pipeline([
    ("kmeans", KMeans(n_clusters=50, random_state=42)),
    ("log_reg", LogisticRegression(multi_class="ovr", solver="lbfgs", max_iter=5000, random_state=42)),
])
pipeline.fit(X_train, y_train)
pipeline_score = pipeline.score(X_test, y_test)
pipeline_score  #0.9777777777777777
#错误率降低了近30%,由3.1%--2.2%。

错误率降低了30!而且此例选择的K值是任意选择的50。

由于K-Means只是分类流水线中的一个预处理步骤,找到一个好的K 比以前简单得多:无需执行轮廓分析或最小化惯性分析,即K 的最优解,是在交叉验证过程中的最佳分类性能的值。使用网格搜索找最佳得K

from sklearn.model_selection import GridSearchCV

param_grid = dict(kmeans__n_clusters=range(2, 100))  #__ 两个下划线
grid_clf = GridSearchCV(pipeline, param_grid, cv=3, verbose=2)  #网格搜索依赖硬件,耗时越10分钟
grid_clf.fit(X_train, y_train)

grid_clf.best_params_
#{'kmeans__n_clusters': 95}
使用聚类进行半监督学习

聚类的另一个用例是在半监督学习中,当我们有大量未标记的实例而仅有少量标记的实例时。

如下逻辑回归模型在只有50个标记实例时的性能:

n_labeled = 50
log_reg = LogisticRegression(multi_class="ovr", solver="lbfgs", random_state=42)
log_reg.fit(X_train[:n_labeled], y_train[:n_labeled])
log_reg.score(X_test, y_test)
#0.8333333333333334

性能不佳~

首先,让我们将训练集聚类为50集群,然后对于每个集群,找到最接近中心点的图像。我们将这些图像称为代表性图像:

k = 50
kmeans = KMeans(n_clusters=k, random_state=42)  
X_digits_dist = kmeans.fit_transform(X_train)   #无监督
print(X_digits_dist.shape)  #(1347, 50) 将1347个实例,聚类对应成50个类
representative_digit_idx = np.argmin(X_digits_dist, axis=0)
#[ 911  559   23  159  736 ... ,距离中心点最小的的50个图像的索引
X_representative_digits = X_train[representative_digit_idx]
#代表性图像

#描图,绘制这50个代表性图像。
plt.figure(figsize=(8, 2))
for index, X_representative_digit in enumerate(X_representative_digits):
    plt.subplot(k // 10, 10, index + 1)
    plt.imshow(X_representative_digit.reshape(8, 8), cmap="binary", interpolation="bilinear")
    plt.axis('off')
plt.show()

y_train[representative_digit_idx]
#查看这50个代表性图像在原始数据集中对应的标签。
#array([4, 8, 0, 6, 8, 3, 7, 7, 9, 2, 5, 5, 8, 5, 2, 1, 2, 9, 6, 1, 1, 6,
#       9, 0, 8, 3, 0, 7, 4, 1, 6, 5, 2, 4, 1, 8, 6, 3, 9, 2, 4, 2, 9, 4,
#       7, 6, 2, 3, 1, 1])

y_representative_digits = np.array([
    4, 8, 0, 6, 8, 3, 7, 7, 9, 2, 5, 5, 8, 5, 2, 1, 2, 9, 6, 1, 1, 6,
    9, 0, 8, 3, 0, 7, 4, 1, 6, 5, 2, 4, 1, 8, 6, 3, 9, 2, 4, 2, 9, 4,
    7, 6, 2, 3, 1, 1])

#为实现半监督学习,构建只有这50个代表图像的标签

在这里插入图片描述

现在我们有了一个只有50个标记实例的数据集,但它们不是完全随机的实例,而是其集群的代表性图像。让我们看看性能是否更好。

log_reg = LogisticRegression(multi_class="ovr", solver="lbfgs", max_iter=5000, random_state=42)
log_reg.fit(X_representative_digits, y_representative_digits)
log_reg.score(X_test, y_test)
#0.9222222222222223  对比前面的0.833  性能提升!

虽然还是在只有50个实例上训练模型,但性能提升很多

由于标记实例通常代价高昂且痛苦,特别是当必须由专家手动完成时,最好让他们标记代表性实例,而不仅仅是随机实例。

DBSCAN算法

该算法将集群定义为高密度的连续区域。工作原理如下

  • 对于每个实例,该算法都会计算在距离它 $\epsilon $ 范围内有多少个实例。该区域称为实例的 $ \epsilon-邻域 $。
  • 如果一个实例在其 $ \epsilon-邻域 $ 内至少包含min_samples 个实例(包括自身),则该实例被视为核心实例。换句话说,核心实例是位于密集区域中的实例。
  • 核心实例附近的所有实例都属于同一集群。这个$ \epsilon-邻域 $ 可能包括其他核心实例。因此,一长串相邻的核心实例形成一个集群。
  • 任何不是核心实例且邻居中没有实例的实例都视为异常。
  • 如果所有集群足够密集并且被低密度区域很好的分隔开,则该算法效果很好。
  • DBSCAN算法虽然有fit_predict()方法,但不能预测新实例隶属哪个集群。可以结合其他算法使用。
from sklearn.datasets import make_moons
from sklearn.cluster import DBSCAN
X, y = make_moons(n_samples=1000, noise=0.05, random_state=42)

dbscan = DBSCAN(eps=0.05, min_samples=5)  #  \epsilon  半径0.05,范围内最少5个实例(含自身)
dbscan.fit(X)
len(dbscan.labels_) #1000,拿到1000个样本的标签
dbscan.labels_[:10] #array([ 0,  2, -1, -1,  1,  0,  0,  0,  2,  5], dtype=int64)  -1集群代表异常

len(dbscan.core_sample_indices_) #核心样本实例808个,core_sample_indices_表示核心实例样本的索引。

dbscan.components_[:3]  #核心实例本身,此例即808个核心实例的坐标。
np.unique(dbscan.labels_) #array([-1,  0,  1,  2,  3,  4,  5,  6], dtype=int64),聚类为8个集群,-1代表异常

dbscan2 = DBSCAN(eps=0.2)  #修改半径 \epsilon为0.2  ,半径扩大
dbscan2.fit(X)
len(dbscan2.core_sample_indices_) # 1000个核心实例
np.unique(dbscan.labels_) # 聚类为2个集群 
 
#描图,左图聚类成8个集群,红叉代表异常,右图完美。

在这里插入图片描述

DBSCAN算法结合K-近邻算法实例代码:
dbscan = dbscan2 # 使用eps=0.2的聚类结果
from sklearn.neighbors import KNeighborsClassifier

knn = KNeighborsClassifier(n_neighbors=50)
knn.fit(dbscan.components_, dbscan.labels_[dbscan.core_sample_indices_])   #使用核心实例训练K近邻

X_new = np.array([[-0.5, 0], [0, 0.5], [1, -0.1], [2, 1]])
knn.predict(X_new)  #用k近邻预测4个新实例 结果:array([1, 0, 1, 0], dtype=int64)
knn.predict_proba(X_new) #查看4个新实例概率,如下
#array([[0.18, 0.82],   ======1
#       [1.  , 0.  ],   ======0
#       [0.12, 0.88],   ======1
#       [1.  , 0.  ]])  ======0
#如图,+号代表4个新实例,绿色区域是0号集群。

在这里插入图片描述

其他的聚类算法
  • 聚集聚类
  • BIRCH
  • 均值漂移
  • 相似性传播
  • 谱聚类
高斯混合模型
EM期望最大化算法:

不仅可以找到集群中心( μ ( 1 ) \mu^{(1)} μ(1) μ ( k ) \mu^{(k)} μ(k)) ,而且可以找到它们的大小、形状和方向( Σ ( 1 ) \Sigma^{(1)} Σ(1) Σ ( k ) \Sigma ^{(k)} Σ(k)),以及它们的相对权重( ϕ ( 1 ) \phi ^{(1)} ϕ(1) ϕ ( k ) \phi ^{(k)} ϕ(k) ) 。

通过设置“协方差类型”超参数,可以对算法查找的协方差矩阵施加约束

  • “full”(默认值):无约束,所有集群都可以采用任何大小的椭球形状。
  • “tied”:所有集群必须具有相同的形状,可以是任何椭球体(即,它们共享相同的协方差矩阵)。
  • “spherical”:所有集群都必须是球形的,但它们可以有不同的直径(即不同的方差)。
  • “diag”:集群可以呈现任何大小的任何椭球形状,但椭球的轴必须与轴平行(即协方差矩阵必须是对角的)。

代码示例(构造数据集)

from sklearn.datasets import make_blobs

X1, y1 = make_blobs(n_samples=1000, centers=((4, -4), (0, 0)), random_state=42)
print(X1.shape)  #(1000,2)
X1 = X1.dot(np.array([[0.374, 0.95], [0.732, 0.598]]))
X2, y2 = make_blobs(n_samples=250, centers=1, random_state=42)
X2 = X2 + [6, -8]
X = np.r_[X1, X2]
y = np.r_[y1, y2]
print(X.shape) #(1250,2)
plt.scatter(X[:,0],X[:,1])

在这里插入图片描述

代码示例(聚类)

from sklearn.mixture import GaussianMixture
gm = GaussianMixture(n_components=3, n_init=10, random_state=42)  # k=3,迭代次数10,默认1
gm.fit(X)

gm.weights_   #得到权重 /phi

gm.covariances_   #得到协方差 /Sigma 

gm.predict(X)  #查看聚类结果(预测结果)

gm.predict_proba(X) #查看聚类到每个集群的概率

在这里插入图片描述

代码示例(调整协方差类型)

gm_full = GaussianMixture(n_components=3, n_init=10, covariance_type="full", random_state=42)
gm_tied = GaussianMixture(n_components=3, n_init=10, covariance_type="tied", random_state=42)
gm_spherical = GaussianMixture(n_components=3, n_init=10, covariance_type="spherical", random_state=42)
gm_diag = GaussianMixture(n_components=3, n_init=10, covariance_type="diag", random_state=42)
gm_full.fit(X)
gm_tied.fit(X)
gm_spherical.fit(X)
gm_diag.fit(X)

在这里插入图片描述

在这里插入图片描述

使用高斯混合进行异常检测

异常检测(也成为离群值检测),是检测严重偏离标准的实例的任务。 正常的实例也称为内值

高斯混合可用于异常检测:位于低密度区域的实例可视为异常。必须定义要使用的密度阈值。例如,在一家试图检测缺陷产品的制造公司中,缺陷产品的比例通常是众所周知的。假设它等于4%,则可以将密度阈值设置为导致4%的实例位于该阈值密度以下区域的值:

densities = gm.score_samples(X)
density_threshold = np.percentile(densities, 4)
anomalies = X[densities < density_threshold]  #得到密度小于4%的点  ,即异常值
#如图红星

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P8RFBh0i-1635767925717)(ml_handout09.assets/mixture_anomaly_detection_plot.png)]

选择聚类数

K-Means 中可以使用惯性或轮廓分数来选择适当的集群数K。

高斯混合中不行,因为当集群不是球形或具有不同大小时,上面方法不可靠

可以尝试找到最小化理论信息量准则的模型,如:贝叶斯信息标准(BIC)或Akaike 信息标准(AIC

gms_per_k = [GaussianMixture(n_components=k, n_init=10, random_state=42).fit(X) for k in range(1, 11)]
#k=1 to 10 尝试10次高斯混合

bics = [model.bic(X) for model in gms_per_k]  
aics = [model.aic(X) for model in gms_per_k]  #得到每个k的bic和aic 绘图如下

plt.figure(figsize=(8, 3))
plt.plot(range(1, 11), bics, "bo-", label="BIC")
plt.plot(range(1, 11), aics, "go--", label="AIC")
plt.xlabel("$k$", fontsize=14)
plt.ylabel("Information Criterion", fontsize=14)
plt.axis([1, 9.5, np.min(aics) - 50, np.max(aics) + 50])
plt.annotate('Minimum',
             xy=(3, bics[2]),
             xytext=(0.35, 0.6),
             textcoords='figure fraction',
             fontsize=14,
             arrowprops=dict(facecolor='black', shrink=0.1)
            )
plt.legend()
plt.show()

在这里插入图片描述

遍历查询集群数和协方差类型超参数的最佳组合

min_bic = np.infty

for k in range(1, 11):
    for covariance_type in ("full", "tied", "spherical", "diag"):
        bic = GaussianMixture(n_components=k, n_init=10,
                              covariance_type=covariance_type,
                              random_state=42).fit(X).bic(X)
        if bic < min_bic:
            min_bic = bic
            best_k = k
            best_covariance_type = covariance_type
best_k
best_covariance_type
# 输出最好的K和最好的协方差类型。
贝叶斯高斯混合模型。
其他用于异常检测和新颖性检测的算法:
  • PCA
  • Fast-MCD(最小协方差决定)
  • 隔离森林
  • 局部离群因子
  • 蛋类SVM
归结演绎推理是一种基于逻辑推理的方法,通过对已知事实和规则进行推理,得出结论。Python可以通过实现归结演绎推理来进行逻辑推理。 以下是一个简单的归结演绎推理的Python实现: 1. 定义规则和事实 ``` # 规则 rules = [ ('penguin', 'bird'), # 企鹅是鸟类 ('bird', 'animal'), # 鸟类是动物 ('lizard', 'animal'), # 蜥蜴是动物 ('bird', 'fly') # 鸟类可以飞 ] # 事实 facts = [ 'tweety is a penguin', # 特维是一只企鹅 'penguin is black and white' # 企鹅是黑白相间的 ] ``` 2. 定义归结演绎推理函数 ``` def resolve(a, b): """ 归结规则 """ for fact_a in a: for fact_b in b: if fact_a == ('not', fact_b) or fact_b == ('not', fact_a): # a和b中有相反的事实,无法归结 continue if fact_a == fact_b: # a和b中有相同的事实,无需归结 continue for i in range(len(fact_a)): if fact_a[i] != fact_b[i]: # 找到不同的部分,进行归结 new_fact = fact_a[:i] + fact_a[i+1:] + fact_b[:i] + fact_b[i+1:] if new_fact not in a and new_fact not in b: # 新的事实不在a和b中,加入a a.append(new_fact) return True return False def resolution(rules, facts): """ 归结演绎推理 """ while True: pairs = [(rules[i], rules[j]) for i in range(len(rules)) for j in range(i+1, len(rules))] new_rules = [] for pair in pairs: a, b = pair new_fact = resolve(a, b) if new_fact: # 归结出新的规则,加入新规则列表 new_rules.append(new_fact) if new_rules: # 有新规则,加入规则列表 rules += new_rules continue for fact in facts: if ('not', fact) in rules: # 有矛盾的事实,返回False return False # 执行完毕,返回True return True ``` 3. 测试归结演绎推理函数 ``` if resolution(rules, facts): print('Tweety can fly') else: print('Tweety cannot fly') ``` 输出结果为:`Tweety can fly`,即特维可以飞。因为根据规则,企鹅是鸟类,鸟类可以飞,所以特维可以飞。 这是一个简单的归结演绎推理的Python实现,可以根据实际需要进行扩展和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值