Introduction to Multi-Armed Bandits——03 Thompson Sampling[1]

news2024/10/5 15:33:56

Introduction to Multi-Armed Bandits——03 Thompson Sampling[1]

参考资料

  1. Russo D J, Van Roy B, Kazerouni A, et al. A tutorial on thompson sampling[J]. Foundations and Trends® in Machine Learning, 2018, 11(1): 1-96.

  2. ts_tutorial

  3. 在线学习(MAB)与强化学习(RL)[4]:贝叶斯Bandit算法

项目代码地址: https://github.com/yijunquan-afk/bandit-learning.git

一、 Greedy Decisions

在第二节中,介绍过贪心算法,其选择动作(action)的两个步骤为:

  1. 从历史数据中估计一个模型

  2. 选择对估计的模型来说是最优的动作。

image-20230118153647817

贪心方法的一个缺点是它没有积极探索,这可能会降低性能。以Bernoulli bandit为例, observationreward,所以 r t = r ( y t ) = y t r_t = r(y_t) = y_t rt=r(yt)=yt。 在每轮 t t t ,将产生对每个 k k k 个动作的平均奖励的估计值 θ ^ k \hat{\theta}_k θ^k,然后选择使这些估计值最大的动作。

我们再用具体的例子进行说明。假设有三个动作(臂),其平均奖励率为 θ ∈ R 3 \theta \in \mathbb{R}^3 θR3 。 特别是,每次臂 k k k 被选中,就会以 θ k \theta_k θk 的概率产生 1 1 1 的奖励,否则奖励为0。 算法事先是不知道 θ \theta θ 的值,需要不断观察样本去估计 θ \theta θ

下图为概率密度函数。这些分布代表了在智能体(agent)分别尝试了动作1和2一千次,动作3三次,获得了累计奖励分别为600、400和1,并将这些观察结果与每个动作的平均奖励的统一先验分布综合起来。智能体确信动作1和动作2的平均奖励接近于他们的期望值,即大约 0.6 0.6 0.6 0.4 0.4 0.4 ,但是对动作3的平均奖励非常不确定。

bernoulli_pdf

因此,对于动作1和动作2我们已经得到了较多的数据,对其分布估计较为准确,所以从贪心的角度考虑,应该抛弃动作2,因为 θ 2 > θ 1 \theta_2 > \theta_1 θ2>θ1 的可能性极小。而由于动作3的数据较少,按理来说应该再尝试多几次,但是贪心算法不可能会这么做,该算法没有考虑到到平均奖励的不确定性

Dithering(抖动)是一种常见的探索方法,它通过随机扰动动作来操作贪心算法。其中一个典型的算法就是 E p s i l o n − g r e e d y Epsilon-greedy Epsilongreedy 算法,上一节说明过。尽管这种形式的探索相对于纯粹的贪心方法可以改善动作,但由于没有 “注销” 动作而浪费了资源。以上图为例,动作2几乎没有机会成为最优选择,因此不值得进行实验,而动作3的不确定性则值得探索。 E p s i l o n − g r e e d y Epsilon-greedy Epsilongreedy 探索将为每个动作分配同等数量的实验,会浪费掉许多实验。1933年提出的汤普森(Thompson Sampling)抽样提供了一个替代抖动的方法,可以更明智地分配探索。

二、Thompson Sampling for the Bernoulli Bandit

2.1 Beta-Bernoulli Bandit

假设有 K K K 的动作,并且在执行时,任何动作都会成功或失败。当进行游戏时,动作 k ∈ { 1 , ⋯   , K } k\in \{1,\cdots,K\} k{1,,K} 产生一个概率为 θ k \theta_k θk的1奖励和一个概率为 1 − θ k 1-\theta_k 1θk 的零奖励。 动作 k ∈ { 1 , ⋯   , K } k\in \{1,\cdots,K\} k{1,,K} 成功的概率为 θ k ∈ [ 0 , 1 ] \theta_k\in[0,1] θk[0,1]。成功概率 ( θ 1 , . . . , θ K ) (\theta_1,...,\theta_K) (θ1,...,θK)对智能体来说是未知的,但随着时间的推移是固定的,因此可以通过实验学习。在第一个阶段,一个动作 x 1 x_1 x1 被应用,奖励 r 1 ∈ { 0 , 1 } r_1 \in \{0,1\} r1{0,1} 以成功概率 P ( r 1 = 1 ∣ x 1 , θ ) = θ x 1 \mathbb{P}(r_1=1 | x_1, \theta) = \theta_{x_1} P(r1=1∣x1,θ)=θx1 产生。 在观察到 r 1 r_1 r1 之后,算法应用另一个动作 x 2 x_2 x2,观察到奖励 r 2 r_2 r2 ,不断进行。

对于每个 θ k \theta_k θk 都有一个先验分布,在Beta-Bernoulli Bandit中,这些先验分布是 beta 分布。参数为 α = ( α 1 , … , α K ) \alpha = (\alpha_1,\ldots,\alpha_K) α=(α1,,αK) β ∈ ( β 1 , … , β K ) \beta \in (\beta_1,\ldots,\beta_K) β(β1,,βK)。对于每个动作 k k k θ k \theta_k θk 先验概率密度函数为:

p ( θ k ) = Γ ( α k + β k ) Γ ( α k ) Γ ( β k ) θ k α k − 1 ( 1 − θ k ) β k − 1 p(\theta_k) = \frac{\Gamma(\alpha_k+\beta_k)}{\Gamma(\alpha_k)\Gamma(\beta_k)} \theta_k^{\alpha_k-1} (1-\theta_k)^{\beta_k-1} p(θk)=Γ(αk)Γ(βk)Γ(αk+βk)θkαk1(1θk)βk1

其中 Γ \Gamma Γ 代表 gamma 函数。随着观察结果的收集,分布会根据贝叶斯规则进行更新。由于beta分布的共轭特性,用它来工作特别方便。特别是,每个动作的后验分布也是带有参数的 beta分布,可以根据一个简单的规则进行更新:

( α k , β k ) ← { ( α k , β k ) if  x t ≠ k ( α k , β k ) + ( r t , 1 − r t ) if  x t = k . \begin{aligned} (\alpha_k, \beta_k) &\leftarrow \begin{cases} (\alpha_k, \beta_k) & \text{if } x_t \neq k \\ (\alpha_k, \beta_k)+(r_t, 1-r_t) & \text{if } x_t =k. \end{cases} \end{aligned} (αk,βk){(αk,βk)(αk,βk)+(rt,1rt)if xt=kif xt=k.

即如果我们选择了臂 k k k ,如果得到奖励为1则将相应的 α k \alpha_k αk +1( β k \beta_k βk 不变),否则将相应的 β k \beta_k βk +1( α k \alpha_k αk不变)。

对于 α k = β k = 1 \alpha_k=\beta_k=1 αk=βk=1的特殊情况,先验 p ( θ k ) p(\theta_k) p(θk) [ 0 , 1 ] [0,1] [0,1]上是均匀的。只有选定的动作的参数被更新。一个参数为 ( α k , β k ) (\alpha_k,\beta_k) (αk,βk) 的 β分布,其平均值为 α k / ( α k + β k ) \alpha_k/(\alpha_k + \beta_k) αk/(αk+βk) , 随着 α k + β k \alpha_k+ β_k αk+βk 的增长,分布变得更加集中。当 α k \alpha_k αk 相较于 β k \beta_k βk 比较大时,平均值比较大,图像右倾,反之左倾,如上图所示。动作1的分布是 b e t a ( 601 , 401 ) beta(601,401) beta(601,401),动作2的分布是 b e t a ( 401 , 601 ) beta(401, 601) beta(401,601),动作3的分布是 b e t a ( 2 , 3 ) beta(2, 3) beta(2,3),也可以看出来动作3的试验次数较少。

2.2 算法思路

在Beta Bernoulli Bandit情形下,贪心算法的伪代码如下:

image-20230118173647747

在每个时间段 t t t,算法产生一个估计值 θ ^ k = α k / ( α k + β k ) \hat{\theta}_k = \alpha_k/(\alpha_k+\beta_k) θ^k=αk/(αk+βk),等于其当前对成功概率 θ k \theta_k θk的期望。 然后,使 θ ^ k \hat{\theta}_k θ^k 最大的动作 x t x_t xt被应用,在这之后得到奖励 r t r_t rt,分布参数 α x t \alpha_{x_t} αxt β x t \beta_{x_t} βxt被更新。

我们再来看看汤普森抽样,其伪代码如下:

image-20230118174258990

与贪心算法类似,唯一的区别是,成功概率估计值 θ ^ k \hat{\theta}_k θ^k 是从后验分布中随机抽取的,后验分布是一个参数为 α k \alpha_k αk β k \beta_k βk 的 β分布,而不是取期望值 α k / ( α k + β k ) \alpha_k / (\alpha_k+\beta_k) αk/(αk+βk)

让我们来看看汤普森采样是如何改进贪心算法的,回到之前三个臂的例子。在这种情况下,贪心算法将放弃学习动作3的潜在宝贵机会。在抖动(dithering)的情况下,尽管探索动作2几乎是徒劳无功的(因为动作2极不可能是最优的),但是探索动作2和3的机会是相等的。而汤普森采样算法会对动作1、2、3进行采样,其概率分别约等于 0.82 0.82 0.82 0 0 0 0.18 0.18 0.18 。 分别该动作抽取的随机估计值超过为其他动作抽取的估计值的概率,等于相应动作是最优的概率。 TS算法会天然同时完成exploreexploit,因为如果一个臂没怎么被选择,那么从这个臂中采样得到的 θ ^ k \hat{\theta}_k θ^k 会以近似均匀的概率落在整个区间上(相当于uniform exploration)。而一个臂如果被选择的次数多了,那么自然估计的就比较准了,如果这个臂比较“好”,则从它的后验分布里采样出来的就有大概率是比较高的,这个臂也就比较容易会被exploit

2.3 python实现与实验结果分析

首先实现两个智能体智能体(agent)——BernoulliBanditEpsilonGreedyBernoulliBanditTS,其中epsilon-greedy的epsilon设置为0就为纯粹的贪心算法。两个智能体之间的区别只在pick_action方法的实现上。

import numpy as np


def random_argmax(vector):
    """选择vector中的最大值的下标,如果有多个最大,则随机选择其中一个"""
    index = np.random.choice(np.where(vector == vector.max())[0])
    return index


class BernoulliBanditEpsilonGreedy():
    """Bernoulli Bandit问题中使用ε-greedy算法"""

    def __init__(self, n_arm, a0=1, b0=1, epsilon=0.0):
        self.n_arm = n_arm
        self.epsilon = epsilon
        self.prior_success = np.array([a0 for arm in range(n_arm)])
        self.prior_failure = np.array([b0 for arm in range(n_arm)])

    def set_prior(self, prior_success, prior_failure):
        # 修改默认先验值
        self.prior_success = np.array(prior_success)
        self.prior_failure = np.array(prior_failure)

    def get_posterior_mean(self):
        # 由beta分布的α和β可知beta分布的期望为 α /(α+β)
        return self.prior_success / (self.prior_success + self.prior_failure)

    def get_posterior_sample(self):
        # 从后验抽样
        return np.random.beta(self.prior_success, self.prior_failure)

    def update_observation(self, observation, action, reward):
        # 简单检查与环境的兼容性
        assert observation == self.n_arm

        if np.isclose(reward, 1):
            self.prior_success[action] += 1
        elif np.isclose(reward, 0):
            self.prior_failure[action] += 1
        else:
            raise ValueError('Rewards should be 0 or 1 in Bernoulli Bandit')

    def pick_action(self, observation):
        if np.random.rand() < self.epsilon:
            # 随机探索
            action = np.random.randint(self.n_arm)
        else:
            # 从后验分布中选取一个均值最大的臂
            posterior_means = self.get_posterior_mean()
            action = random_argmax(posterior_means)

        return action


class BernoulliBanditTS(BernoulliBanditEpsilonGreedy):
    def pick_action(self, observation):
        """汤普森抽样"""
        sampled_means = self.get_posterior_sample()
        action = random_argmax(sampled_means)
        return action

然后实现环境(environment):

class BernoulliBandit():
  """Bernoulli Bandits"""

  def __init__(self, probs):
    """   
    probs: 每个臂的奖励概率
    optimal_reward: 最大奖励值
    n_arm: 臂的数目 
    """
    self.probs = np.array(probs)
    self.optimal_reward = np.max(self.probs)
    self.n_arm = len(self.probs)

  def get_observation(self):
    return self.n_arm

  def get_optimal_reward(self):
    return self.optimal_reward

  def get_expected_reward(self, action):
    return self.probs[action]

  def get_stochastic_reward(self, action):
    # 重复伯努利试验,产生0/1的奖励
    return np.random.binomial(1, self.probs[action])
  
  def advance(self, action, reward):
    """Updating the environment (useful for nonstationary bandit)."""
    pass

然后设计实验方法(experiment):

import numpy as np
import pandas as pd

class BaseExperiment(object):
  """基础实验:记录悔值(regret和采取的动作(action)
  """

  def __init__(self, agent, environment, n_steps,
               seed=0,  unique_id='NULL'):
    """
    agent: 智能体
    environment: 环境
    n_steps: 迭代次数
    seed: 随机数种子
    unique_id: 标识实验

    results: 实验结果

    """
    self.agent = agent
    self.environment = environment
    self.n_steps = n_steps
    self.seed = seed
    self.unique_id = unique_id

    self.results = []
    self.data_dict = {}


  def run_step_maybe_log(self, t):
    # 观察环境,选择臂
    observation = self.environment.get_observation()
    action = self.agent.pick_action(observation)

    # 计算有用的值
    optimal_reward = self.environment.get_optimal_reward()
    expected_reward = self.environment.get_expected_reward(action)
    reward = self.environment.get_stochastic_reward(action)

    # 使用获得的奖励和选择的臂更新智能体
    self.agent.update_observation(observation, action, reward)

    # 记录悔值
    instant_regret = optimal_reward - expected_reward
    self.cum_regret += instant_regret

    # 环境进化(非平稳实验中才会用到)
    self.environment.advance(action, reward)

    # 记录产生的数据
    self.data_dict = {'t': (t + 1),
                        'instant_regret': instant_regret,
                        'cum_regret': self.cum_regret,
                        'action': action,
                        'unique_id': self.unique_id}
    self.results.append(self.data_dict)


  def run_experiment(self):
    """运行实验,收集数据"""
    np.random.seed(self.seed)
    self.cum_regret = 0
    self.cum_optimal = 0

    for t in range(self.n_steps):
      self.run_step_maybe_log(t)

    # 使用pandas存储数据
    self.results = pd.DataFrame(self.results)

将TS算法与贪心算法的模拟结果进行比较。考虑一个三臂 beta-Bernoulli bandit,其平均奖励为 θ 1 = 0.9 \theta_1=0.9 θ1=0.9, θ 2 = 0.8 \theta_2=0.8 θ2=0.8, 以及 θ 3 = 0.7 \theta_3=0.7 θ3=0.7。 让每个臂平均奖励的先验分布是均匀的(即beta(1,1))。 下图绘制了基于每种算法的200次独立模拟的结果。每次模拟都是在一千个时间段内进行的。

import plotnine as gg

def plotComparation(algorithm = 'TS'):
    probs = [0.9,0.8,0.7]
    n_steps = 1000
    N_JOBS = 200

    results = []
    for job_id in range(N_JOBS):
        if(algorithm == 'TS'):
            agent = BernoulliBanditTS(n_arm=len(probs))
        else:
            agent = BernoulliBanditEpsilonGreedy(n_arm=len(probs))
        env = BernoulliBandit(probs)
        experiment = BaseExperiment(
            agent, env, n_steps=n_steps, seed=job_id, unique_id=str(job_id))
        experiment.run_experiment()
        results.append(experiment.results)


    df_agent = pd.concat(results)


    n_action = np.max(df_agent.action) + 1
    plt_data = []
    for i in range(n_action):
        probs2 = (df_agent.groupby('t') # 按照t分组
                .agg({'action': lambda x: np.mean(x == i)}) # 按照action求平均
                .rename(columns={'action': 'action_' + str(i + 1)}))  # 重命名列
        plt_data.append(probs2)
    # 重置索引
    plt_df = pd.concat(plt_data, axis=1).reset_index()

    p = (gg.ggplot(pd.melt(plt_df, id_vars='t'))
        + gg.aes('t', 'value', colour='variable', group='variable')
        + gg.geom_line(size=1.25, alpha=0.75)
        + gg.xlab('time period (t)')
        + gg.ylab('Action probability')
        + gg.ylim(0, 1)
        + gg.scale_colour_brewer(name='Variable', type='qual', palette='Set1'))
    print(p)

plotComparation('Greedy')
plotComparation('TS')

pngpngAS

而书中的试验次数达到了10000次,其结果如下:

image-20230119220203199

从图中可以看到,贪心算法最后并非收敛到动作1,而是三个动作都有可能选上。TS算法最后收敛到了动作1,即最优动作。

而在线学习通常用悔值图来分析比较,接下来进行实验分析(进行200次实验)。

def plotCompara2():
    probs = [0.9,0.8,0.7]
    n_steps = 1000
    N_JOBS = 200

    results1 = []
    for job_id in range(N_JOBS):
        agent = BernoulliBanditTS(n_arm=len(probs))
        env = BernoulliBandit(probs)
        experiment = BaseExperiment(
            agent, env, n_steps=n_steps, seed=job_id, unique_id=str(job_id))
        experiment.run_experiment()
        results1.append(experiment.results)

    df_agent1 = (pd.concat(results1)).assign(agent='TS')

    results2 = []
    for job_id in range(N_JOBS):
        agent = BernoulliBanditEpsilonGreedy(n_arm=len(probs))
        env = BernoulliBandit(probs)
        experiment = BaseExperiment(
            agent, env, n_steps=n_steps, seed=job_id, unique_id=str(job_id))
        experiment.run_experiment()
        results2.append(experiment.results)
    df_agent2 = (pd.concat(results2)).assign(agent='greedy')


    df_agents = pd.concat([df_agent1,df_agent2])

    plt_df = (df_agents.groupby(['t','agent'])
            .agg({'instant_regret': np.mean})
            .reset_index())
    p = (gg.ggplot(plt_df)
       + gg.aes('t', 'instant_regret', colour='agent')
       + gg.geom_line(size=1.25, alpha=0.75)
       + gg.xlab('time period (t)')
       + gg.ylab('per-period regret')
       + gg.scale_colour_brewer(name='agent', type='qual', palette='Set1'))
    print(p)

plotCompara2()

png

由上图可知,TS的平均悔值随着时间的推移而消失,而贪婪算法则不是这样的。

如果固定臂产生奖励的概率,那结果将很大程度受到 θ \theta θ 的影响。 因此, θ \theta θ 的选值要足够合理。一个自然的方法是对先验分布中的许多 θ \theta θ 实例进行抽样,并为每个实例生成一个独立的模拟。 下图绘制了200个这样的模拟的平均。

def plotCompara3():
    n_steps = 1000
    N_JOBS = 200

    results1 = []
    for job_id in range(N_JOBS):
        probs = np.random.rand(3)
        agent = BernoulliBanditTS(n_arm=len(probs))
        env = BernoulliBandit(probs)
        experiment = BaseExperiment(
            agent, env, n_steps=n_steps, seed=job_id, unique_id=str(job_id))
        experiment.run_experiment()
        results1.append(experiment.results)

    df_agent1 = (pd.concat(results1)).assign(agent='TS')

    results2 = []
    for job_id in range(N_JOBS):
        agent = BernoulliBanditEpsilonGreedy(n_arm=len(probs))
        env = BernoulliBandit(probs)
        experiment = BaseExperiment(
            agent, env, n_steps=n_steps, seed=job_id, unique_id=str(job_id))
        experiment.run_experiment()
        results2.append(experiment.results)
    df_agent2 = (pd.concat(results2)).assign(agent='greedy')


    df_agents = pd.concat([df_agent1,df_agent2])

    plt_df = (df_agents.groupby(['t','agent'])
            .agg({'instant_regret': np.mean})
            .reset_index())
    p = (gg.ggplot(plt_df)
       + gg.aes('t', 'instant_regret', colour='agent')
       + gg.geom_line(size=1.25, alpha=0.75)
       + gg.xlab('time period (t)')
       + gg.ylab('per-period regret')
       + gg.scale_colour_brewer(name='agent', type='qual', palette='Set1'))
    print(p)

plotCompara3()

png
​ 书上经过10000次模拟的结果如下,结果类似,只是曲线更为平滑。

image-20230120193756508

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/174111.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript基础知识点整理(一)——数据类型、判定、转换、this指向

JavaScript是每一位前端开发者都必备的技能&#xff0c;接下来会分章节文章阐述介绍每一部分的内容。 JavaScript基础整理①1、JavaScript类型2、原始&#xff08;primitive&#xff09;类型3、对象&#xff08;Object&#xff09;类型4、类型判定4.1、typeof4.2、instanceof4.…

Vue2基础、组件化编程、脚手架、Vuex、Vue路由、UI组件库

尚硅谷张天禹老师讲课 学习视频 1、Vue简介 Vue2中文官网 1.1 Vue 介绍 一套用于构建用户界面的渐进式JavaScript框架 构建用户界面&#xff1a;把数据通过某种办法变成用户界面渐进式&#xff1a;可以自底向上逐层的应用&#xff0c;简单应用只需要一个轻量小巧的核心库&…

SQL--DQL

目录 1、基础查询 1. 查询多个字段 1. 举例 2. 举例 2. 字段设置别名 1. 举例 2. 举例 3. 去除重复记录 1. 举例 2、条件查询 1. 等于&#xff08;&#xff09; 2. 小于&#xff08;<&#xff09; 3. 小于等于&#xff08;<&#xff09; 4. 没有&#xff…

Java设计模式中命令模式是怎么回事/命令模式如何使用,什么场景适用

继续整理记录这段时间来的收获&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 6.4 命令模式 6.4.1 定义 将一个请求封装为一个对象&#xff0c;使发出请求的责任和执行请求的责任分隔开&#xff0c;方便了将命令对象进行存储&#xff0c;传递…

激活函数(26个)

最近在学习网络&#xff0c;发现一会这个网络用了这个激活函数&#xff0c;一会那个网络用了那个激活函数&#xff0c;这些激活函数都有什么作用啊&#xff0c;不知道&#xff0c;这里学习一下&#xff0c;整理下来&#xff0c;方便以后查阅。 激活函数&#xff08;26个&#x…

Elasticsearch7.8.0版本高级查询——组合查询文档

目录一、初始化文档数据二、组合查询文档2.1、概述2.2、示例一、初始化文档数据 在 Postman 中&#xff0c;向 ES 服务器发 POST 请求 &#xff1a;http://localhost:9200/user/_doc/1&#xff0c;请求体内容为&#xff1a; {"name":"张三","age"…

ruoyi_cloud==启动

1-下载ruoyi_cloud源码&#xff0c;新建数据库 下载ruoyi_cloud源码&#xff0c; 新建数据库ry-config&#xff0c;并在数据库中执行ruoyi-cloud源码中ry_config_20220929.sql &#xff0c; 即导入若依这个项目的一些交给nacos管理的配置信息&#xff0c;后面nacos会来读取。…

[java/初学者]类的继承

前言 继承是OOP的三大特征之一&#xff0c; 它的作用是在现有类的基础上派生出一个新的类&#xff0c;这个类中继承了现有类的数据属性和行为&#xff0c;并且能进行扩展。 基于这一特性&#xff0c;我们将前者命名为“父类”或者“基类”&#xff0c;而后者则因此命名为“子…

【matplotlib】20.其他图

#【matplotlib】20.其他图 2023.1.20 polt为点线图&#xff0c;后面我们看看plt里面的其他图 20.1 Scatter 散点图 plt.scatter() import matplotlib.pyplot as plt import numpy as npn 1024x np.random.normal(0,1,n) y np.random.normal(0,1,n)# 颜色 这个应该是个tan值…

Java多线程02(生命周期,线程安全synchronized(),锁Lock )

目录1.线程的生命周期2.线程安全和同步代码块synchronized()3.Lock锁1.线程的生命周期 2.线程安全和同步代码块synchronized() synchronized(锁){ 操作共享数据的代码 }//保证线程安全 锁默认打开&#xff0c;有一个线程进去了&#xff0c;锁自动关闭。里面的代码全部执行完毕…

DFS(一)深度优先搜索(Depth First Search)一条道走到黑

目录 一、盒子与扑克牌 二、员工的重要性 三、图像渲染 一、盒子与扑克牌 假如有编号为1~3的3张扑克牌和编号为1~3的3个盒子&#xff0c;现在需要将3张牌分别放到3个盒子中去&#xff0c;且每个盒子只能放 一张牌&#xff0c;一共有多少种不同的放法。 当走到一个盒子面前的…

【Python-Django】医疗辅助平台-数据库设计-day2.1

操作需知&#xff1a; MySQL存储很长500的汉字选用字符类型_mysql字符串太长用什么存_信息时代弄潮儿的博客-CSDN博客 mysql中的Varchar(255)可以放多少个汉字_Ecloss的博客-CSDN博客_varchar255能存多少汉字 MySQL中的Text类型_SlowIsFastLemon的博客-CSDN博客_text类型 …

自制win10 PE usb启动盘教程

拿出祖传的U盘&#xff08;16GB&#xff09;安装老白菜根据教程制作U盘然后在U盘ios文件夹里面放入从系统之家下载的win10 ios文件包然后把U盘插入需要安装的电脑上面重启电脑按住F10打开BIOS设置界面USB启动设置为<启用>将UEFI和传统模式下的&#xff1a;U盘/USB硬盘USB…

【数据结构前言】

前言&#xff1a; 在之前我们已经有了C语言的基础&#xff0c;掌握了一些基本知识过后我们就可以进行其他方面的学习了&#xff0c;继我们学完C语言之后我们将会学习数据结构的相关知识&#xff0c;今天先让大家对其进行初步的认识&#xff01; 目录1. 什么是数据结构&#xff…

SpringCloud(13):分布式配置中心

1 为什么需要分布式配置中心&#xff1f; 在分布式系统中&#xff0c;由于服务数量巨多&#xff0c;为了方便服务配置文件统一管理&#xff0c;所以需要分布式配置中心组件。在Spring Cloud中&#xff0c;有分布式配置中心组件spring cloud confifig &#xff0c;它支持配置服…

增量式PID控制算法及仿真

当执行机构需要的是控制量的增量&#xff08;例如驱动步进电机&#xff09;时&#xff0c;应采用增量式PID控制。根据递推原理可得&#xff1a;增量式PID控制算法:根据增量式PID控制算法&#xff0c;设计了仿真程序&#xff0c;被控对象如下:PID控制参数:kp8&#xff0c;ki 0.1…

普中科技MicroPython基于esp32的基础教程-02

容器类型数据 序列 存放多值的连续内存空间&#xff0c;并且通过编号访问&#xff0c;其实就是类似于C语言的数组&#xff0c;相同数据类型、连续内存空间、使用下标访问 序列索引 支持正负操作&#xff0c;0到N&#xff0c;-N到-1 &#xff0c;但是C语言不支持负操作 个人觉…

温度、压力、物位仪表工作原理

温度、压力、物位仪表工作原理 一、温度仪表 1、温度仪表通常分一次仪表与二次仪表&#xff0c;一次仪表通常为&#xff1a;热电偶、热电阻、双金属温度计、就地温度显示仪等。二次仪表通常为温度记录仪、温度巡检仪、温度显示仪、温度调节仪、温度变送器等。 2、温度测量仪表…

Java基础进阶

Stream流 引例 需求&#xff1a;按照下面要求完成集合的创建和遍历 创建一个集合&#xff0c;存储多个字符串元素 1. 把所有以“曹”开头的元素存储到新集合中 2. 把曹开头&#xff0c;长度为3的元素存储到新集合中 List<String> list List.of("曹操", "…

【6s965-fall2022】剪枝✂pruningⅡ

剪枝比例 问题&#xff1a;我们应该如何找到每层的剪枝比率&#xff1f; 较浅的层&#xff0c;低层次的特征较深的层&#xff0c;抽象的特征 问题&#xff1a;哪些层的冗余度最高&#xff1f; 非统一剪枝&#xff08;每一层的稀疏度不一样&#xff09;比统一剪枝&#xff0…