可解释的AI:用LIME解释扑克游戏

可解释的AI(XAI)一直是人们研究的一个方向,在这篇文章中,我们将看到如何使用LIME来解释一个模型是如何学习扑克规则的。在这个过程中,我们将介绍:

  • 如何将LIME应用到扑克游戏中;
  • LIME如何工作;
  • LIME 的优点和缺点是什么。

将LIME应用到扑克游戏中

目标

我们的目标是建立一个可以预测扑克牌的模型。“五张”使一种扑克牌的游戏规则,其中的组合决定了你是否赢得了这一轮的比赛。

我们手里有五张牌,这里我们的目标是希望模型能预测手里有哪一手牌。

数据集

我们的数据来自UCI机器学习库(https://archive.ics.uci.edu/ml/datasets/Poker+Hand)。在数据集中,牌是通过从花色中分离出秩(卡片的值)进行编码的。

为了确保有足够的数据来训练模型,我们使用了一百万行的数据集用于训练,在下面的图片中展示了一些例子:

模型

使用硬编码决定你哪一手牌的规则是很容易的。从顺子到四张,根据规则排序即可。但是当我们想要通过一个模型来学习规则时,就比较复杂了,但是如果我们成功的训练好了这个模型,我们就可以将这种方法应用于任何扑克游戏中,不需要管理分类的基本规则是什么。

对于模型,我们选择了一个随机森林分类器。使用hyperopt对模型的超参数进行了调优。加权f1得分为0.75,可以合理预测给定5张牌作为输入的扑克牌。在本文末尾会有完整的代码

LIME

使用LIME来确定为什么我们的模型会做出这样的预测。哪些牌以及为什么主导了这次预测结果?这就是可以利用LIME的地方。

LIME通过在原始模型之上训练一个可解释模型来工作。这样,即使原始模型不能告诉你它为什么预测结果,你也可以使用LIME来确定是什么影响了它的决策。我们将使用这个逻辑来确定为什么这个随机森林分类器预测某些结果。

现在让我们看看他是如何工作的:

上面的分类器预测我们的牌是”一对“。为什么会这样预测呢?看看LIME解释:

LIME构建了一个可视化的图。在垂直轴上是特征值:显示手中的牌的数字和花色。在横轴上是各种特征值对分类的贡献。这些贡献值被缩放为相同的维度,并显示一个特征是有利于预测(绿色),还是不利于预测(红色)。

我们的第一手牌是一对,你可能会认为两个a的贡献最大。但是LIME告诉我们情况并非如此。在上面的图表中,LIME认为第3张牌对分类的贡献最大(尽管是负贡献)。如果不使用可解释的AI,我们根本没法想到这是为什么。研究为什么这个确切的特征触发了LIME模型是做进一步探索性数据分析的一个极好的切入点。

我们再继续研究另外一套:

使用LIME解释

可以看到牌的数字比花色对同花顺的分类贡献更大。对于我们的理解这简直是不可能的,因为同花顺就是要有相同的花色。但是通过使用LIME,我们可以看到实际上是卡片数字被赋予了分类更多的权重。如果不使用可解释的AI,我们很容易忽略这一点,但通过使用LIME,我们可以确保自己的假设得到验证。

LIME帮助解释为什么模型会做出这样的预测。无论使用它来确认模型是否触发了我们所期望的功能,还是作为探索性分析的一部分,LIME都是都是一个强大的方法。

通过上面的两个例子,我们可以看到LIME通过在原始模型之上训练一个可解释模型来工作。即使原始模型不能告诉你它为什么预测结果,你也可以使用LIME来确定是什么影响了它的决策。

LIME是如何工作的

为什么要使用黑盒模型呢?就模型性能而言,黑盒模型通常比白盒模型具有优势。但是它们的缺点就是可解释性较低。2016年引入了LIME作为解决黑箱模型不透明问题的方法。为了理解LIME在后台做了什么,让我们来看看LIME是如何工作的:

上图解释了LIME的概念,在使用LIME时需要考虑以下因素。

优点:

  • LIME可以在广泛的数据集上很好地工作
  • LIME比数学上更完整的方法(如SHAP值)要快得多
  • 解释特定结果的LIME方法不会改变,即使底层黑盒模型改变了

缺点:

  • LIME模型不能保证揭示所有的潜在决策
  • LIME模型只能局部应用,而不能全局应用

本文代码

最后就是本文的代码了

 from ctypes import alignment
 from functools import partial
 
 import matplotlib.pyplot as plt
 import numpy as np
 import pandas as pd
 from hyperopt import STATUS_OK, Trials, fmin, hp, space_eval, tpe
 from hyperopt.pyll import scope
 from lime import lime_tabular
 from sklearn.ensemble import RandomForestClassifier
 from sklearn.model_selection import train_test_split
 from sklearn.metrics import f1_score
 
 
 def objective(params:dict, X_train:pd.DataFrame, y_train:pd.DataFrame, X_val:pd.DataFrame, y_val:pd.DataFrame)->dict:
     """This function is used as objecive for the hyperparameter tuning
     Parameters
     ----------
     params : dict
         parameters for the model
     X_train : pd.Dataframe
         Feature dataset for training
     y_train : pd.DataFrame
         Target variable for training
     X_val : pd.DataFrame
          Feature dataset for validation
     y_val : pd.DataFrame
         Target variable for validation
     Returns
     -------
     dict
         loss and status for hyperopt
     """
 
     # define the model
     model = RandomForestClassifier(random_state=1, **params)
 
     # train the model
     model.fit(X_train,y_train)
 
     # validate and get the score
     score = model.score(X_val, y_val)
 
     return {"loss": -score, "status": STATUS_OK}
 
 def find_best_parameters(seed:int=2, **kwargs)->dict:
     """In this function hpo is performed
     Parameters
     ----------
     seed : int, optional
         random seed, by default 2
     Returns
     -------
     dict
         best paramers found by hyperopt
     """
     
     # initialize trials
     trial = Trials()
 
     # initialize the objetve function
     partial_objective = partial(
             objective,
             X_train=kwargs['X_train'],
             y_train=kwargs['y_train'],
             X_val=kwargs['X_val'],
             y_val=kwargs['y_val']
         )
 
     # initialize the search space for hyperopt
     params = {'n_estimators': scope.int(hp.quniform('n_estimators', 100, 500, 10)),
               'max_depth': scope.int(hp.quniform('max_depth', 5, 60, 2)),
               'min_samples_leaf': scope.int(hp.quniform('min_samples_leaf', 1, 10, 1)),
               'min_samples_split': scope.int(hp.quniform('min_samples_split', 2, 10, 1))}
 
     # find best params
     best_argmin = fmin(
             fn=partial_objective,
             space=params,
             algo=tpe.suggest,
             max_evals=50,
             trials=trial,
             rstate=np.random.default_rng(seed),
         )  
 
     best_params = space_eval(params, best_argmin)
     
     return best_params
 
 # Tweak the output to make it look nicer
 def as_pyplot_figure(
     exp, classif, classes_names, instance_to_explain, label:int=1, figsize=(4, 4)
 ):
     """This function has been taked from the lime package and tweaked for this particular use case
     Parameters
     ----------
     exp : _type_
         lime explanation of the instance to explain
     classif : _type_
         clssification type
     classes_names : _type_
         names of the classrs
     instance_to_explain : _type_
         the instance of the data which should be explained
     label : int, optional
         label for protting - of the explanation instance, by default 1
     figsize : tuple, optional
         desired size of pyplot in tuple format, defaults to (4,4).
     Returns
     -------
     _type_
         figure with the explanations
     """
     
 
     # find the explanation for a particular label
     exp_list = exp.as_list(label=label)  
     fig, ax = plt.subplots(figsize=figsize)
     vals = [x[1] for x in exp_list]
     names = [x[0] for x in exp_list]
 
     # plot the contributions
     vals.reverse()
     names.reverse()
     colors = ["green" if x > 0 else "red" for x in vals]
     pos = np.arange(len(exp_list)) + 0.5
     ax.barh(pos, vals, align="center", color=colors)
     ax.set_yticks(pos, labels=names)
     limit = max(abs(min(vals)), abs(max(vals)))
     ax.set_xlim(left=-limit, right=limit)
     ax.set_xticks([])
     ax.set_xlabel("Contribution")
 
     # Add second axis with the values of the cards
     suits = {1: "\u2661", 2: "\u2660", 3: "\u2662", 4: "\u2663"}
     ranks = {
         1: "Ace",
         2: "Two",
         3: "Three",
         4: "Four",
         5: "Five",
         6: "Six",
         7: "Seven",
         8: "Eight",
         9: "Nine",
         10: "Ten",
         11: "Jack",
         12: "Queen",
         13: "King",
     }
 
     # etract the data from the explanation 
     list_figures = []
     for i in exp_list:
 
 
         if "S" in i[0]:
             if '=' in i[0]:
 
                 # logic for categorical
                 new_string = i[0][i[0].index("S") :]
                 extract = int(new_string[ new_string.index("=")+1:])
                 list_figures.append(suits[extract])
 
             else:
 
                 # logic for continuous variables
                 new_string = i[0][i[0].index("S") :]
                 extract = new_string[: new_string.index(" ")]
                 list_figures.append(suits[instance_to_explain.loc[extract]])
 
         elif "R" in i[0]:
 
             if '=' in i[0]:
 
                 # logic for categorical
                 new_string = i[0][i[0].index("R") :]
                 extract = int(new_string[ new_string.index("=")+1:])
                 list_figures.append(ranks[extract])
                 
             else:
 
                 # logic for continous variables
                 new_string = i[0][i[0].index("R") :]
                 extract = new_string[: new_string.index(" ")]
                 list_figures.append(ranks[instance_to_explain.loc[extract]])
 
     # create second axis
     ax2 = ax.twinx()
     ax2.set_yticks(ticks=np.arange(len(exp_list)) + 0.5, labels=list_figures[::-1])
     ax2.barh(pos, vals, align="center", color=colors)
 
     # add title
     if classif == "classification":
         title = f"Why {classes_names[label][4:]}?"
     else:
         title = "Local explanation"
     plt.title(title)
     plt.tight_layout()
 
     return fig
 
 
 # Read dataset
 df_test = pd.read_csv("./data/df_test.csv")
 df_train = pd.read_csv("./data/df_train.csv")
 
 # Let's take the suit and the rank (value) of each card
 col_names = ["S1", "R1", "S2", "R2", "S3", "R3", "S4", "R4", "S5", "R5", "y"]
 df_train.columns = col_names
 df_test.columns = col_names
 
 # Define our hand combinations
 target_labels = [
     "0 - High card",
     "1 - One pair",
     "2 - Two pairs",
     "3 - Three of a kind",
     "4 - Straight",
     "5 - Flush",
     "6 - Full house",
     "7 - Four of a kind",
     "8 - Straight flush",
     "9 - Royal flush",
 ]
 
 # get the training and validation sets
 y = df_train["y"]
 X = df_train.drop(columns="y")
 X_train, X_val, y_train, y_val = train_test_split(
     X, y, test_size=0.3, random_state=1
 )
 
 # find best parameters
 best = find_best_parameters(X_train=X_train, X_val=X_val, y_train=y_train, y_val=y_val)
 
 # Get test data
 y_test = df_test["y"]
 X_test = df_test.drop(columns="y")
 
 # Get train data
 y_train = df_train["y"]
 X_train = df_train.drop(columns="y")
 
 # Fit with a black-box model on full train dataset
 model = RandomForestClassifier(random_state=42, **best)
 model.fit(X_train, y_train)
 
 # get the F1-score of the model on the test set
 y_pred = model.predict(X_test)
 f1score = f1_score(y_test, y_pred, average='weighted')
 
 # define instances to explain (any instance from train / test can be taken here)
 instance_1 = pd.Series({'S1': 2, 'R1': 2,
                         'S2': 4, 'R2': 3,
                         'S3': 4, 'R3': 7,
                         'S4': 4, 'R4': 1,
                         'S5': 2, 'R5': 1})
 
 instance_2 = pd.Series({'S1': 4, 'R1': 2,
                         'S2': 4, 'R2': 3,
                         'S3': 4, 'R3': 4,
                         'S4': 4, 'R4': 5,
                         'S5': 4, 'R5': 10})
 
 # initialise LIME
 explainer = lime_tabular.LimeTabularExplainer(
     training_data=np.array(X_train),
     feature_names=X_train.columns,
     class_names=target_labels,
     mode="classification",
     categorical_features= [i for i in range(10)]
 )
 
 for instance_to_explain, label in zip([instance_1, instance_2], [1, 5]):
 
     # create explanation
     exp = explainer.explain_instance(
         data_row=instance_to_explain, predict_fn=model.predict_proba, num_features=10, labels=[label]
     )
 
     # visualize: using lime show_in_noteboook()
     exp.show_in_notebook(show_table=True)
 
     # visualize using the custom visualization
     as_pyplot_figure(exp=exp, classif="classification", classes_names=target_labels, instance_to_explain=instance_to_explain, label=label);

如果你需要你也可以在这里找到它

https://gist.github.com/O-Konstantinova/153284d4ec81e5ca6c9b049277117434#file-lime-poker-py

作者:Olga Konstantinova

本文转载于网络 如有侵权请联系删除

相关文章

  • Java中怎样由枚举常量的ordinal值获得枚举常量对象

    大家好,又见面了,我是全栈君Java1.5提供了关键字enum,能够通过该关键字方便得定义自己须要的枚举类型,比方enumSeason{SPRING,SUMMER,AUTUMN,WINTER}就定义了一个季节枚举类型。在本例中,对于Season.SPRING这个对象,Season.SPRING.name()能够得到该对象的字符串,即“SPRING”;反之,由Season.valueOf(“SPRING”)则能够得到该对象,即Season.SPRING。也就是说,使用name()方法和valueOf(String)方法能够在枚举类型对象和字符串之间方便得转换。当然,假设valueOf(String)方法的參数不是该枚举类型合法的字符串,则会抛出IllegalArgumentException异常。对于枚举类型,Java内部实际上还是转换为java.lang.Enum的子类,能够通过“javap-cSeason”命令反编译来观察这一点。Enum类提供了一个ordinal()方法,用来返回枚举对象的序数,比方本例中SPRING,SUMMER,AUTUMN,WINTER的序数就分别为0,1,2

  • 对长虹而言,鲲鹏是桩好生意

    一间百平米的展厅,几台滚动着政务动态的显示屏,外加中岛上摆放着的PC机和打印设备……在智慧城市建设创新不断的今天,当人们用所有这些来展示一个城市智慧政务的部分建设成果时,它们显得一切都那么顺理成章,这就是绵阳市这个中国科技城和长虹公司想要的效果。没有什么不同——如果使用者和观者感受到了这一点,那么决策者的所有投入就有了价值。也不是完全相同。在展区一边的三台PC中,中间那台在打开文档和视频文件的速度上,就稍优于另两台同配置市售竞品。那是硬件针对软件的优化所产生的效果。这台品牌为“天宫”的PC机使用了华为提供的鲲鹏主板。相邻的展台上,还摆放着一台使用鲲鹏主板的“天宫”牌服务器。在12月18日绵阳市的“鲲鹏生态长虹天宫系统应用示范工程”发布中,它们作为功臣被展示出来。它们是华为今年9月宣布“硬件开放、软件开源、使能合作伙伴”后的新晋,由长虹公司推出。在那次发布会中,华为开始正式支持生态伙伴基于鲲鹏主板,开发自有品牌的服务器和台式机产品。清华同方当时在第一时间作出了响应,宣布推出基于鲲鹏主板的PC产品。因为华为提供了主板接口规范、参考设计指南和软硬件兼容性列表等技术支持,品牌供应商的开发与投产

  • 【老张监控技术】Zabbix 5.0 Alpha1版本试用

    作者:张世宏(当代张思德) Zabbix社区专家,他从事IT运维工作7年,不仅是cactifans作者,还是go语言爱好者,Devops实践者,使用zabbix6年,具有丰富的使用经验和二次开发经验。点击查看精彩峰会演讲Zabbix5.0Alpha1版本试用Zabbix5.0Alpha1于2020.年1月20日发布。作为5.0的的第一个版本,可抢先体验5.0的部分功能。安装本次使用源码编译进行安装,与之前版本安装方法变化不大。ZabbixServer下载后解压。Zabbix5.0可使用agent2,因此使用以下参数编译安装./configure--prefix=/usr/local/zabbix--enable-server--enable-agent2--with-mysql--with-net-snmp--with-libcurl--with-libxml2复制配置 安装之后,修改zabbix_server.conf的数据库相关信息,启动zabbixserver即可。/usr/local/zabbix/sbin/zabbix_server-c/usr/local/zabbix/et

  • 001.MySQL高可用主从复制简介

    一简介1.1概述Mysql内建的复制功能是构建大型,高性能应用程序的基础。将Mysql的数据分布在多个系统之上,这种分布的机制,是通过将Mysql的某一台主机的数据复制到其它主机(slaves)上,并重新执行一遍来实现的。复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器。主服务器将更新写入二进制日志文件,并维护文件的一个索引以跟踪日志循环。这些日志可以记录发送到从服务器的更新。当一个从服务器连接主服务器时,它通知主服务器从服务器在日志中读取的最后一次成功更新的位置。从服务器接收从那时起发生的任何更新,然后封锁并等待主服务器通知新的更新。二技术原理2.1支持的复制类型基于语句的复制:在主服务器上执行的SQL语句,在从服务器上执行同样的语句。MySQL默认采用基于语句的复制,效率比较高,一旦发现没法精确复制时,会自动选着基于行的复制。基于行的复制:把改变的内容复制过去,而不是把命令在从服务器上执行一遍.从mysql5.0开始支持。混合类型的复制:默认采用基于语句的复制,一旦发现基于语句无法精确的复制时,就会采用基于行的复制。2.2技术特点MySQL复制技术有以下一些特点

  • 6.1 集合和映射--集合->底层基于二叉搜索树实现

    前言:在第5章的系列学习中,已经实现了关于二叉搜索树的相关操作,详情查看第5章即可。在本节中着重学习使用底层是我们已经封装好的二叉搜索树相关操作来实现一个基本的集合(set)这种数据结构。 集合set的特性: 集合Set存储的元素是无序的、不可重复的。为了能达到这种特性就需要寻找可以作为支撑的底层数据结构。 这里选用之前自己实现的二叉搜索树,这是由于该二叉树是不能盛放重复元素的。因此我们可以使用二叉搜索树这种底层来实现集合(set)。1、集合set相关功能1.1add()方法特性二分搜索树的添加操作add:不能盛放重复元素2.set应用典型应用:1.客户统计2.词汇量统计3.集合实现3.1Set接口定义/** *集合的接口 */ publicinterfaceSet<E>{ voidadd(Ee);//添加<——<不能添加重复元素 voidremove(Ee);//移除 intgetSize();//获取大小 booleanisEmpty();//是否为空 booleancontains(Ee);//是否包含元素 }复制3.2基于二分搜索树实现集合Set//基

  • Git 常用命令合集

    $gitinit     建立git仓库(一般都是在github上新建好,直接克隆到本地)$gitclone**.git   克隆git仓库$gitadd-A  全部或者***(文件名) $gitcommit-m"***" 提交修改,备注***$gitpush(-uoriginmaster)可选  本地仓库推送到远程仓库$gitsubtreepush--prefix=distorigingh-pages 将子目录dist文件夹推送到远程分支gh-pages $gitstatus   查看运行结果$gitdiff***  查看***(文件名)的修改内容$gitlog    查看历史记录$gitreset --hardcommit_id 回退到commit_id的版本$gitreflog    查看命令历史,以便撤销回退$gitcheckout--*** 撤销修改,回到最近一次gitcommit或gitadd时的状态$gitrm***  删除***(文件名)$ssh-keygen-trsa-C"***" 创建SSHKey$gitremoteaddor

  • PHP中的数据库三、redis

    memcache虽然好用,解决了数据库遇到高并发时的IO问题,但还有很多问题丞待解决:1、数据持久性问题,memcache用内存进行存储,一旦memcache服务器宕机,那么所存储的数据全部丢失。2、memcache存储的数据类型单一,只支持key-value型的数据,要存储复杂类型的数据,必然需要PHP脚本的大量逻辑操作。redis基本介绍redis也是一个内存非关系型数据库,它拥有memcache在数据存储上的全部优点,而且在memcache的基础上(memcache的介绍可以看我的上一篇博文:PHP中的数据库二、memcache增加了数据持久性功能,redis用rdb和aof两种方式实现数据持久性,在服务器突然宕机时也能几乎保留已存的全部数据。增加了string(字符串)、set(集合)、sorted_set(有序集合)、hash(哈希)、list(链表)数据类型,方便了多类型的存储和数据库操作。增加了安全验证(可为服务器设置连接密码)。redis的主从分离等系统更完善(官方开发)。原生支持发布/订阅、队列、缓存等工具。当然,相比较memcache,它的数据库操作也较为复杂。re

  • 一次完整的 Django 项目的迁移,有关 MySQL 数据库的导出与导入

    我的Django项目做了一次实际的项目移植,就是把同一个项目连同数据库中存储的信息迁移到另外一个环境中。具体是把服务器上面的数据库迁移到了本地,也就是Linux服务器到本地Windows,这篇文章就来简述一下我具体的操作过程。Django项目文件的迁移关于项目文件的迁移没必要做过多的说明,因为一般这种操作都是依靠Github去克隆或者pull就行了,对应我这个博客项目,完整的操作可以查看我的Github的博客项目介绍。MySQL数据库的迁移因为我的博客使用的数据库是MySQL,所以这里主要还是说一下这个数据库的说明。首先,需要强调的我的数据库使用的编码格式是utf8,这需要mysql:5.7以上的版本,我当时创建数据库的语句是下面这句:CREATEDATABASE`izone`DEFAULTCHARACTERSETutf8COLLATEutf8_general_ci;复制导出数据库首先,需要在服务器上面把已经存了很多信息的数据库导出来,方法很简单,但是我还是在这里遇到了坑(其实是个笑话,我对mysql用的不多,都是需要用的时候去查,或者用的Python操作的数据库),具体的就是本来导出

  • 【设计模式】-行为型-10-备忘录模式

    主要角色 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。 代码示例 packagememento; publicclassMementoPattern { publicstaticvoidmain(String[]args) { Originatoror=newOriginator(); Caretakercr=newCaretaker(); or.setState("S0"); System.out.println("初始状态:"+or.getState()); cr.setMemento(or.createMemento());//保存状态 or.setState("S1"); System.out.println("新的状态:"+or.getState());

  • Visual C++安装失败解决:Error 0x80240017: Failed to execute MSU package.

    MicrosoftVisualC++2015Redistributable(x64)安装失败 查看日志出现下面错误 Error0x80240017:FailedtoexecuteMSUpackage. 需要依次安装下面补丁: 1、KB2919442 https://www.microsoft.com/zh-cn/download/details.aspx?id=42153 2、KB2919355 https://www.microsoft.com/en-us/download/details.aspx?id=42334 必须按以下顺序安装更新:clearcompressionflag.exe、KB2919355、KB2932046、KB2959977、KB2937592、KB2938439和KB2934018。 3、KB2999226 https://www.microsoft.com/en-us/download/confirmation.aspx?id=49063复制  

  • SQL中使用WITH AS提高性能 简化嵌套SQL(转载)

    一.WITHAS的含义   WITHAS短语,也叫做子查询部分(subquery factoring),可以让你做很多事情,定义一个SQL片断,该SQL片断会被整个SQL语句所用到。有的时候,是为了让SQL语句的可读性更高些, 也有可能是在UNIONALL的不同部分,作为提供数据的部分。特别对于UNIONALL比较有用。因为UNION ALL的每个部分可能相同,但是如果每个部分都去执行一遍的话,则成本太高,所以可以使用WITHAS短语,则只要执行一遍即可。如果WITH AS短语所定义的表名被调用两次以上,则优化器会自动将WITH AS短语所获取的数据放入一个TEMP表里,如果只是被调用一次,则不会。而提示materialize则是强制将WITH AS短语里的数据放入一个全局临时表里。很多查询通过这种方法都可以提高速度。二.使用方法先看下面一个嵌套的查询语句: select*fromperson.StateProvincewhereCountryRegionCodein       &nb

  • python字体高亮显示

    参数: --显示方式:0(默认值)、1(高亮)、22(非粗体)、4(下划线)、24(非下划线)、5(闪烁)、25(非闪烁)、7(反显)、27(非反显) --前景色:30(黑色)、31(红色)、32(绿色)、33(黄色)、34(蓝色)、35(洋红)、36(青色)、37(白色) --背景色:40(黑色)、41(红色)、42(绿色)、43(黄色)、44(蓝色)、45(洋红)、46(青色)、47(白色) 常见开头格式: \033[0m          默认字体正常显示,不高亮 \033[32;0m      红色字体正常显示 \033[1;32;40m   显示方式:高亮。字体前景色:绿色。背景色:黑色; 例子: print('\033[31;1m你好,中国!\033[0m')print('\033[32;1m你好,中国!\033[0m')结果:复制 print('\033[41;1m你好,中国!\03

  • 洛谷P5281 [ZJOI2019] Minimax搜索

    设修改前根节点的权值为\(W\),不难发现,一定存在一条从根到叶子的链,链上的每个点权值都为\(W\),那么只要这条链上任意一点权值改变,根节点权值最后一定会改变。 考虑一个叶子\(i\),且\(w_i\neqW\),要想通过改变该点权值来让链上的点发生改变,该点必须满足以下一个情况: \(w_i<W\),且该点祖先和链的第一个交点的深度为奇数,这时将其改为\(W+1\)。 \(w_i>W\),且该点祖先和链的第一个交点的深度为偶数,这时将其改为\(W-1\)。 直接处理代价为\(k\)的情况不好处理,考虑求出代价\(\leqslantk\)的方案后差分。 将问题转化为概率来考虑,链上的点设\(f_x\)表示\(x\)子树内的叶子通过代价\(\leqslantk\)的修改后\(x\)权值不变的概率,得最终方案数为总方案乘\(1-\prodf_x\)。考虑不在链上的点,若其深度为奇数,\(DP\)值为权值\(<W\)的概率,若其深度为偶数,\(DP\)值为权值\(>W\)的概率,得转移为: \[\largef_x=\prod_{y\inson_x}1-f_y \]

  • CefSharp容易忽视掉的一个问题debug.log

    CefSharp的debug.log 作用 1、通常用来记录CefSharp的调试日志,包括控制台内的一些日志输出。例如warn、Info、error等等。 2、也是为了让开发者可以更好的排查出CefSharp在运行过程中的一些错误。如下图所示: 我这日志都是一些乱码、没去转码。当然可以从他的开头可以看出他记录下了Info的日志。 问题 由于CefSharp的这一块日志是默认开启的。所以但在使用程序的时候,多多少少都会记录下日志,时间久了日志会积压特别多,如下图所示:  今天无意间发现程序主目录里有个文档高达103MB。这才发现CefSharp一个默认的问题就是日志会默认记录。 解决方式 解决方法也很简单,如下代码:  就是在CefSharp的初始化里加上这句话,当然还有可以选择只记录的类型,希望对CefSharp的开发者有一定的帮助。

  • python基础之socket编程part2---粘包和并发

    粘包现象 基于tcp的套接字实现远程执行命令的操作(1.执行错误命令。2.执行ls。3.执行ipconfig) #_*_coding:utf-8_*_ __author__='777' importsocket importsubprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.bind(("127.0.0.1",8080)) phone.listen(5) print("等待命令....") whileTrue: conn,addr=phone.accept() whileTrue: try: date=conn.recv(1024) s=date.decode("utf8") res=subprocess.Popen("%s"%(s),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,) re1=res.stdout.read() re2=res.stderr.read() iflen(re1)==0andlen(re2)==0

  • InheritableThreadLocal类

    作用:子线程可以访问父线程设置的本地变量。 应用场景: 子线程需要使用存放在threadLocal变量中的用户登录信息; 中间件需要把统一的id追踪的整个调用链路记录下来。 实现原理(代码分析):

  • 团队编程项目作业5

      学号:2015035107184 得分:9.5   原因:表现非常好   学号:2015035107024 得分:9 原因:能力强,能提出关键问题。   学号:2015035107082 得分:9  原因:认真,努力。   学号:2015035107086 得分:8    原因:互相讨论。   学号:2015035107108 得分:8   原因:认真检查。   学号:2015035107111 得分:8   原因:努力修改。

  • 2017-09-20 课后作业-阅读任务-阅读笔记-2

    我今天阅读了构建之法的第八章需求分析: 1获取和引需导求:找到软件的利益相关者,引导他们表达出真实的需求软件企业=软件+商业模式 2分析和定义需求:从各个方面获取需求惊醒规整,定义需求的内涵 3验证需求:通过分析报告、技术原型、用户调查或演示等形式向客户反映我们对需求的认知 4在软件产品的生命周期中管理需求:需求在发展变化,技术在发展,团队成员的能力也在提高  

  • python爬虫——汽车之家数据

    相信很多买车的朋友,首先会在网上查资料,对比车型价格等,首选就是“汽车之家”,于是,今天我就给大家扒一扒汽车之家的数据: 一、汽车价格: 首先获取的数据是各款汽车名称、价格范围以及最低指导价: defget_oa_price(self): try: oa_price_data_list=[] forpageinrange(1,27): oa_price_api=f"https://price.16888.com/gz/search-0-0-0-0-0-0-0-0-0-1-0-0-0-0-{page}.html" response=self.sc.get_html(oa_price_api) ifnotresponse: print('城市页请求失败') return0 #燃油车数据块 oa_data_=re.findall(r'<divclass="style-box">\s+<ulclass="clearfix">([\s\S]*?)</ul>',response.text)[0] #燃油车id和名字列表 car_id_name_list=re.

  • 【实践报告】Linux基础实践一

        【chmod命令】  chmod命令是非常重要的,用于改变文件或目录的访问权限。用户用它控制文件或目录的访问权限。  该命令有两种用法。一种是包含字母和操作符表达式的文字设定法;另一种是包含数字的数字设定法。   (1).文字设定法  chmod[who][+/-/=][mode]文件名[who]  操作对象who可是下述字母中的任一个或者它们的组合:  u表示“用户(user)”,即文件或目录的所有者。  g表示“同组(group)用户”,即与文件属主有相同组ID的所有用户。  o表示“其他(others)用户”。  a表示“所有(all)用户”。它是系统默认值。[+/-/=]  操作符号可以是:  +添加某个权限。  -取消某个权限。  =赋予给定权限并取消其他所有权限(如果有的话)。[mode]  设置mode所表示的权限可用下述字母的任意组合:  r可读。  w可写。  x可执行。  X只有目标文件对某些用户是可执行的或该目标文件是目录时才追加x属性。  s在文件执行时把进程的属主或组ID置为该文件的文件属主。方式“u+s”设置文件的用户ID位,“

  • 存文件

    packagecom.lovo.cunwenjian; importjava.io.FileInputStream; importjava.io.FileNotFoundException; importjava.io.FileOutputStream; importjava.io.IOException; importjava.util.Properties; importjava.util.Set; publicclassCunwenjian{  publicstaticvoidmain(String[]args){   //TODOAuto-generatedmethodstub   Propertiesprops=newProperties();   props.setProperty("NOT1","娜娜");   props.setProperty("NOT2","二硕");   props.setProperty("NOT3","贤贤");   

相关推荐

推荐阅读