【Gradio】Building With Blocks 块中的状 态 + 动态应用程序与渲染装饰器

State in Blocks  块中的状态

我们介绍了接口中的状态,这个指南将看看块中的状态,其工作原理大致相同。

 全局状态 

块中的全局状态与接口中的工作原理相同。在函数调用外创建的任何变量都是所有用户共享的引用。

会话状态 

Gradio 支持会话状态,在块应用程序中,数据在页面会话中多次提交后仍然持续存在。重申一下,会话数据不会在模型的不同用户之间共享。要在会话状态中存储数据,您需要做三件事:

  1. 创建一个 gr.State() 对象。如果这个有状态的对象有默认值,请将其传递给构造函数。

  2. 在事件监听器中,将 State 对象作为输入和输出。

  3. 在事件监听器函数中,将变量添加到输入参数和返回值中。

让我们来看一场挂人游戏。

import gradio as gr  # 导入gradio库secret_word = "gradio"  # 设置游戏的秘密单词with gr.Blocks() as demo:    # 使用gr.Blocks创建一个应用布局  used_letters_var = gr.State([])  # 使用状态变量记录已使用过的字母with gr.Row() as row:  # 创建一行用于并排放置输入框和显示框with gr.Column():  # 创建第一列,用于放置输入文本框和猜测按钮input_letter = gr.Textbox(label="Enter letter")  # 创建输入文本框,用于输入字母btn = gr.Button("Guess Letter")  # 创建按钮,用于提交猜测的字母with gr.Column():  # 创建第二列,用于放置挂人游戏进度和已使用字母显示框hangman = gr.Textbox(label="Hangman",value="_"*len(secret_word)  # 初始化挂人游戏的显示,用下划线代表秘密单词的每个字母)used_letters_box = gr.Textbox(label="Used Letters")  # 创建一个文本框,用于显示已猜过的字母def guess_letter(letter, used_letters):  # 定义猜测字母的函数used_letters.append(letter)  # 将猜测的字母添加到已使用字母列表answer = "".join([(letter if letter in used_letters else "_")  # 根据已猜测的字母更新挂人游戏的显示for letter in secret_word])return {used_letters_var: used_letters,  # 更新状态变量中的已使用字母列表used_letters_box: ", ".join(used_letters),  # 更新已使用字母文本框的内容hangman: answer  # 更新挂人游戏显示框的内容}btn.click(guess_letter,  # 将按钮点击事件绑定到guess_letter函数[input_letter, used_letters_var],  # 指定输入参数:输入的字母和已使用字母列表[used_letters_var, used_letters_box, hangman]  # 指定更新的目标组件:状态、已使用字母显示框和挂人游戏显示框)
demo.launch()  # 启动应用

这段代码使用Gradio库创建了一个Web应用程序,实现了一个简单的挂人(Hangman)游戏。游戏的目标是猜测一个秘密单词,这里秘密单词被设置为"gradio"。

  • 用户通过输入框输入一个字母,点击"Guess Letter"按钮提交猜测。

  • 游戏记录用户已经猜测过的字母,并更新挂人进度显示以及显示已用字母列表。如果猜测的字母在秘密单词中,那么对应位置的下划线会被替换为实际字母;否则,猜测的字母只会出现在已用字母列表中。

  • guess_letter函数处理字母猜测过程,包括更新游戏进度和已用字母。每次猜测后,游戏状态通过Gradio的State组件进行更新,以保留游戏进度。

  • GUI布局采用了行和列的组织方式,以清晰地分隔输入、游戏进度显示和已用字母列表。

这个示例展示了Gradio如何用于实现交互式Web应用,以及如何利用状态管理维护应用的动态数据。

02543b69ae70d92c9296e0fbd68c2e80.png

让我们看看我们如何在这个游戏中执行上面列出的 3 个步骤:

  1. 我们在 used_letters_var 中存储使用过的字母。在 State 的构造函数中,我们将这个的初始值设置为 [] ,一个空列表。

  2. 在 btn.click() 中,我们在输入和输出中都有对 used_letters_var 的引用。

  3. 在 guess_letter 中,我们将这个 State 的值传递给 used_letters ,然后在返回语句中返回这个 State 的更新值。

对于更复杂的应用程序,您可能会在单个 Blocks 应用程序中有许多 State 变量存储会话状态。 

了解更多关于 State 的信息,请查阅文档。https://gradio.app/docs/state

Dynamic Apps with the Render Decorator

动态应用程序与渲染装饰器

在 Blocks 中定义的组件和事件监听器到目前为止都是固定的 - 一旦演示启动,就不能添加新的组件和监听器,也不能移除已有的。

@gr.render 装饰器引入了动态更改这一能力的可能。让我们来看一下。

动态组件数量 

在下面的示例中,我们将创建一个可变数量的文本框。当用户编辑输入文本框时,我们会为输入中的每个字母创建一个文本框。请在下面尝试:

import gradio as gr  # 导入gradio库with gr.Blocks() as demo:  # 使用gr.Blocks创建一个应用布局input_text = gr.Textbox(label="input")  # 创建一个输入文本框,用于输入字符串@gr.render(inputs=input_text)  # 使用gr.render修饰器绑定输入和渲染逻辑def show_split(text):  # 定义处理和展示分割文本的函数if len(text) == 0:  # 检查输入文本长度,如果为空则显示提示信息return gr.Markdown("## No Input Provided")  # 使用Markdown组件显示没有输入的提示信息else:components = []  # 初始化一个组件列表,用于存放每个字符对应的Textbox组件for letter in text:  # 遍历输入文本的每个字符components.append(gr.Textbox(value=letter))  # 为每个字符创建一个Textbox组件,并将其添加到列表中return components  # 返回包含所有字符Textbox组件的列表demo.launch()  # 启动应用

需要注意的是,原始代码中的 show_split 函数在条件分支中直接使用了 gr.Markdown 和 gr.Textbox 创建新组件的方式,但在 gr.render 修饰的函数中,正确的做法是返回渲染组件对象(例如,gr.Markdown 或者一组 gr.Textbox 组件)而不是直接在函数中实例化这些组件。

此代码的功能是:用户在Web应用中输入一个字符串,应用将根据这个字符串的每个字符创建一个单独的 Textbox 组件展示出来。如果用户没有输入任何文本,应用将显示一个Markdown组件提醒用户“没有提供输入”。

这个示例展示了Gradio中如何利用装饰器来处理用户输入,并根据输入动态生成和渲染组件,从而增加应用的交互性和动态性。

946bd78347d59c00e38ebe612faf8b8c.png

acca86ae8defe4d9b07e13469e185baf.png

看看我们现在如何使用自定义逻辑创建可变数量的文本框 - 在这种情况下,一个简单的 for 循环。 @gr.render 装饰器通过以下步骤实现这一点:

  1. 创建一个函数并将@gr.render 装饰器附加到它上。

  2. 将输入组件添加到@gr.render 的 inputs= 参数中,并为函数中的每个组件创建一个相应的参数。任何对组件的更改都会自动重新运行此函数。

  3. 根据输入添加函数内部想要渲染的所有组件。

现在,每当输入改变时,函数就会重新运行,并用最新运行的结果替换之前函数运行创建的组件。非常直接!让我们给这个应用程序增加一点更复杂的内容:

import gradio as gr  # 导入gradio库with gr.Blocks() as demo:  # 使用gr.Blocks创建一个应用布局input_text = gr.Textbox(label="input")  # 创建一个输入文本框,用于输入字符串mode = gr.Radio(["textbox", "button"], value="textbox")  # 创建单选按钮,让用户选择输出模式(文本框或按钮)@gr.render(inputs=[input_text, mode], triggers=[input_text.submit])  # 使用gr.render修饰器绑定输入和触发逻辑def show_split(text, mode):  # 定义处理和展示分割文本的函数if len(text) == 0:  # 检查输入文本长度,如果为空则显示提示信息return gr.Markdown("## No Input Provided")  # 使用Markdown组件显示没有输入的提示信息else:components = []  # 初始化一个组件列表,用于存放根据字符和模式生成的组件for letter in text:  # 遍历输入文本的每个字符if mode == "textbox":  # 如果用户选择的是文本框模式components.append(gr.Textbox(value=letter))  # 则为每个字符创建一个Textbox组件,并将其添加到列表中else:  # 如果用户选择的是按钮模式components.append(gr.Button(letter))  # 则为每个字符创建一个Button组件,并将其添加到列表中return components  # 返回包含所有根据模式生成的组件的列表demo.launch()  # 启动应用

这段代码使用Gradio库创建了一个Web应用程序,允许用户输入一个字符串并选择输出模式(文本框或按钮)。应用将根据用户输入的字符串,针对每个字符动态创建并展示对应的组件,这些组件类型取决于用户通过单选按钮选择的模式。

  • input_text 是用户输入字符串的文本框。

  • mode 是一个单选按钮组,允许用户选择输出模式,即是显示文本框还是按钮。

  • show_split 函数根据用户输入的字符串和所选择的模式动态生成组件。如果字符串为空,则显示一个Markdown组件提醒用户“没有提供输入”;否则,根据选择的模式为字符串中的每个字符生成对应的文本框或按钮。

这个示例展示了Gradio中如何处理复杂的条件逻辑来动态渲染不同种类的组件,以及如何基于用户的选择和输入来调整应用界面的展示内容,提高用户交互体验。

df059ff9ad874e579bfb8697868f2e6b.png

1a2fd275aeae475e6eaa2fcec08f2e3a.png

默认情况下 @gr.render 重新运行是由应用程序的 .load 监听器和任何提供的输入组件的 .change 监听器触发的。我们可以通过在装饰器中显式设置触发器来覆盖这一点,就像我们在这个应用程序中所做的那样,只在 input_text.submit 上触发。如果您正在设置自定义触发器,并且还希望在应用程序开始时自动渲染,请确保将 demo.load 添加到您的触发器列表中。

动态事件监听器

如果您正在创建组件,您可能也想为它们附加事件监听器。让我们看一个例子,它接受可变数量的文本框作为输入,并将所有文本合并到一个框中。

import gradio as gr  # 导入gradio库with gr.Blocks() as demo:  # 使用gr.Blocks创建一个应用布局text_count = gr.State(1)  # 使用状态变量记录文本框的数量,初始为1add_btn = gr.Button("Add Box")  # 创建一个按钮,用于增加文本框add_btn.click(lambda x: x + 1, text_count, text_count)  # 将按钮点击事件绑定到lambda函数,每次点击时将文本框数量加1# 定义函数,根据当前文本框数量动态生成文本框@gr.render(inputs=text_count)def render_count(count):boxes = []  # 初始化一个列表,用于存放动态生成的文本框for i in range(count):  # 根据文本框的数量循环生成文本框box = gr.Textbox(key=i, label=f"Box {i}")  # 为每个文本框设置唯一的key和标签boxes.append(box)  # 将生成的文本框添加到列表中# 定义函数,用于合并所有文本框中的文本def merge(*args):return " ".join(args)  # 将所有文本框的内容用空格连接起来merge_btn.click(merge, boxes, output)  # 将"Merge"按钮绑定到merge函数,将所有文本框的内容合并并显示在输出框中merge_btn = gr.Button("Merge")  # 创建一个用于触发合并操作的按钮output = gr.Textbox(label="Merged Output")  # 创建一个文本框,用于显示合并后的文本demo.launch()  # 启动应用

这段代码展示了如何使用Gradio库创建一个动态的Web应用程序,它允许用户通过点击按钮动态增加文本框,并且提供了一个合并按钮来合并所有文本框中的内容。

  • 应用程序初始时,有一个文本框和两个按钮:一个用于增加文本框的 "Add Box" 按钮,另一个用于合并所有文本框内容的 "Merge" 按钮。

  • 每次用户点击 "Add Box" 按钮,通过修改状态变量 text_count 来记录当前文本框的数量,随后动态在界面上渲染相应数量的文本框。

  • "Merge" 按钮绑定了一个函数,该函数将所有创建的文本框中的文本合并,并将合并后的结果显示在一个标记为 "Merged Output" 的输出文本框中。

96130aa7040b753b6346aa17c0fef864.png

5351773bf6a4da8393eabe8d5c720087.png

让我们看看这里发生了什么:

  1. 状态变量 text_count 用于跟踪要创建的文本框数量。通过点击添加按钮,我们增加 text_count ,这触发了渲染装饰器。

  2. 请注意,在我们在渲染函数中创建的每一个文本框中,我们都明确设置了一个 key= 参数。这个键允许我们在重新渲染之间保留这个组件的值。如果您在文本框中输入一个值,然后点击添加按钮,所有的文本框都会重新渲染,但它们的值不会被清除,因为 key= 保持了一个组件在渲染过程中的值。

  3. 我们已经将创建的文本框存储在一个列表中,并将这个列表作为输入提供给合并按钮事件监听器。请注意,所有使用在渲染函数内部创建的组件的事件监听器也必须在该渲染函数内部定义。事件监听器仍然可以引用渲染函数外部的组件,就像我们在这里所做的,通过引用 merge_btn 和 output ,它们都是在渲染函数外部定义的。

正如组件一样,每当函数重新渲染时,前一次渲染创建的事件监听器会被清除,最新运行的新事件监听器会被附加上。

这使我们能够创建高度可定制和复杂的互动!

整合在一起  Putting it Together

让我们来看两个使用以上所有功能的例子。首先,尝试下面的待办事项应用程序:

d61b2e9694e4f6e5edfc45832f54e10f.png

import gradio as gr  # 导入gradio库with gr.Blocks() as demo:  # 开启一个使用Blocks的Gradio应用布局tasks = gr.State([])  # 使用State存储任务清单,初始化为空列表new_task = gr.Textbox(label="Task Name", autofocus=True)  # 创建一个文本框用于输入新任务名称,自动聚焦# 定义添加新任务的函数def add_task(tasks, new_task_name):return tasks + [{"name": new_task_name, "complete": False}], ""  # 在任务清单中添加一项新任务,并重置输入框# 将新任务的文本框的提交动作绑定到add_task函数new_task.submit(add_task, [tasks, new_task], [tasks, new_task])# 定义渲染待办事项的函数@gr.render(inputs=tasks)def render_todos(task_list):complete = [task for task in task_list if task["complete"]]  # 过滤出已完成的任务incomplete = [task for task in task_list if not task["complete"]]  # 过滤出未完成的任务# 显示未完成任务的标题和数量gr.Markdown(f"### Incomplete Tasks ({len(incomplete)})")for task in incomplete:with gr.Row():  # 对于每个未完成的任务,创建一个行容器gr.Textbox(task['name'], show_label=False, container=False)  # 显示任务名称的文本框# 创建完成任务的按钮done_btn = gr.Button("Done", scale=0)def mark_done(task=task):  # 定义标记任务完成的函数task["complete"] = Truereturn task_listdone_btn.click(mark_done, None, [tasks])  # 绑定按钮点击到mark_done函数# 创建删除任务的按钮delete_btn = gr.Button("Delete", scale=0, variant="stop")def delete(task=task):  # 定义删除任务的函数task_list.remove(task)return task_listdelete_btn.click(delete, None, [tasks])  # 绑定按钮点击到delete函数# 显示已完成任务的标题和数量gr.Markdown(f"### Complete Tasks ({len(complete)})")for task in complete:gr.Textbox(task['name'], show_label=False, container=False)  # 为每个已完成的任务显示一个文本框以展示任务名称demo.launch()  # 启动Gradio应用

这段代码通过Gradio库实现了一个简单的待办事项(To-Do List)应用。用户可以输入新的待办任务名称,待办任务将被添加到清单中并显示在页面上。每个待办事项旁边有两个按钮:“Done”和“Delete”。点击"Done"按钮将标记任务为已完成,点击"Delete"按钮则将任务从清单中删除。整个待办事项清单被分成两部分显示:未完成的任务和已完成的任务。

此应用主要展示了Gradio的State使用来存储和更新任务清单,以及如何使用Buttons和Textboxes创建交互式界面。这是一个典型的示例,展示了通过简单的Gradio界面实现复杂的应用逻辑。

请注意,几乎整个应用程序都在一个单一的 gr.render 内部,该 gr.render 会对任务 gr.State 变量做出反应。这个变量是一个嵌套列表,这带来了一些复杂性。如果您设计一个 gr.render 来响应列表或字典结构,请确保执行以下操作:

  1. 任何修改状态变量的事件监听器,如果这种修改应该触发重新渲染,必须将状态变量设置为输出。这让 Gradio 知道要检查变量是否在后台发生了变化。

  2. 在一个 gr.render 中,如果循环中的变量在事件监听函数内部使用,那么这个变量应该通过在函数头部将其设置为默认参数来“冻结”。看看我们是如何在 mark_done 和 delete 中都有 task=task 。这将变量冻结为其“循环时”的值。

让我们来看一个最后的例子,它使用了我们学到的所有东西。下面是一个音频混音器。提供多个音轨并将它们混合在一起。

45fb56c251d39167bf1bc86bea2f061b.png

这段代码通过Gradio库实现了一个音轨合并应用。用户可以通过点击"Add Track"按钮来动态增加需要合并的音轨数量。对于每个音轨,用户可以上传音频文件并设置音量。应用中提供了"Merge Tracks"按钮来启动音轨合并的操作,合并后的音轨将会在页面下方的"Output"音频播放器中播放。

具体实现包括:

  • 使用State记录音轨数量,初始值为1。

  • 通过点击“Add Track”按钮,动态增加音轨数量。

  • 对于每个音轨,提供了文件上传(Audio)、音轨名称(Textbox)和音量控制(Slider)的功能。

  • "Merge Tracks"按钮绑定的merge函数处理所有音轨的合并逻辑。该函数遍历每个音轨,根据其音量调整音频数据,然后将所有音轨数据进行合并。

  • 合并音轨时通过NumPy处理音频数据,包括调整音量和音轨长度匹配,以及将多个音轨叠加。

  • 最终合并后的音轨将在"Output"音频播放器中播放给用户听。

注意:为确保代码正确执行,merge_btn.click(merge, set(audios + volumes), output_audio)这行代码应放在所有组件(特别是audios和 volumes列表)完全定义之后。

import gradio as gr  # 导入Gradio库
import numpy as np  # 导入NumPy库,用于处理音频数据with gr.Blocks() as demo:  # 使用gr.Blocks创建一个应用布局track_count = gr.State(1)  # 使用State记录音轨数量,默认为1add_track_btn = gr.Button("Add Track")  # 创建一个按钮,用于增加音轨数量# 将按钮点击事件绑定到一个lambda函数,每次点击时将音轨数量加1add_track_btn.click(lambda count: count + 1, track_count, track_count)# 定义函数,根据当前音轨数量动态生成音轨输入和音量滑块@gr.render(inputs=track_count)def render_tracks(count):audios = []  # 初始化一个列表,用于存放音轨输入组件volumes = []  # 初始化一个列表,用于存放音量滑块组件with gr.Row():  # 创建一个行容器for i in range(count):  # 根据音轨数量循环生成对应的音轨with gr.Column(variant="panel", min_width=200):  # 为每个音轨创建一个列容器gr.Textbox(placeholder="Track Name", key=f"name-{i}", show_label=False)  # 创建一个文本框,用于输入音轨名称track_audio = gr.Audio(label=f"Track {i}", key=f"track-{i}")  # 创建一个音频输入组件track_volume = gr.Slider(0, 100, value=100, label="Volume", key=f"volume-{i}")  # 创建一个音量滑块组件audios.append(track_audio)  # 将音频输入组件添加到列表中volumes.append(track_volume)  # 将音量滑块组件添加到列表中# 定义音轨合并函数def merge(data):sr, output = None, Nonefor audio, volume in zip(audios, volumes):  # 遍历音轨和音量组件sr, audio_val = data[audio]  # 获取当前音轨的采样率和数据volume_val = data[volume]  # 获取当前音轨的音量final_track = audio_val * (volume_val / 100)  # 根据音量调整音轨数据if output is None:output = final_track  # 如果输出为空,则当前音轨数据作为输出else:# 调整音轨长度,确保所有音轨长度一致min_shape = tuple(min(s1, s2) for s1, s2 in zip(output.shape, final_track.shape))trimmed_output = output[:min_shape[0], ...][:, :min_shape[1], ...] if output.ndim > 1 else output[:min_shape[0]]trimmed_final = final_track[:min_shape[0], ...][:, :min_shape[1], ...] if final_track.ndim > 1 else final_track[:min_shape[0]]output = trimmed_output + trimmed_final  # 将当前音轨数据加到输出中return (sr, output)  # 返回最终合并的音轨数据merge_btn.click(merge, set(audios + volumes), output_audio)  # 将合并按钮点击事件绑定到merge函数merge_btn = gr.Button("Merge Tracks")  # 创建一个按钮,用于启动音轨合并操作output_audio = gr.Audio(label="Output", interactive=False)  # 创建一个音频输出组件,用于播放合并后的音轨demo.launch()  # 启动应用

在这个应用程序中需要注意两件事:

我们为所有组件提供了key=!我们需要这样做,以便在为现有轨道设置值之后添加另一个轨道时,我们对现有轨道的输入值在重新渲染时不会被重置。

当传递给事件监听器的组件类型多样且数量任意时,使用集合和字典表示法输入比使用列表表示法更为简便。如上所述,我们在将输入传递给合并函数时,制作了一个包含所有输入gr.Audio和gr.Slider组件的大集合。在函数体中,我们将组件值作为字典查询。

gr.render大大扩展了gradio的功能 - 看看你能从中创造出什么!

0f45fa680c3f9c592d725cc6928b8bb9.png

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分布式锁三种方案

基于数据库的分布式锁(基于主键id和唯一索引) 1基于主键实现分布式锁 2基于唯一索引实现分布式锁 其实原理一致,都是采用一个唯一的标识进行判断是否加锁。 原理:通过主键或者唯一索性两者都是唯一的特性,如果多个…

Linux远程管理日志

实验介绍 本实验旨在实现主机将日志远程发送到堡垒机或远程服务器上,实现通过一台机器管理整个网络内的主机的效果。 准备两台虚拟机作为生产主机和管理机,保证网络通畅,展示如下: 关闭firewalld,通过配置rsyslog&a…

【查缺补漏】python

python查缺补漏 底板除 还有一种除法是//,称为地板除,两个整数的除法仍然是整数: >>> 10 // 3 3你没有看错,整数的地板除//永远是整数,即使除不尽。要做精确的除法,使用/就可以。 因为//除法只…

react用ECharts实现组织架构图

找到ECharts中路径图。 然后开始爆改。 <div id{org- name} style{{ width: 100%, height: 650, display: flex, justifyContent: center }}></div> // data的数据格式 interface ChartData {name: string;value: number;children: ChartData[]; } const treeDep…

主窗体设计

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 Python、QT与PyCharm配置完成后&#xff0c;接下来需要对快手爬票的主窗体进行设计&#xff0c;首先需要创建主窗体外层为&#xff08;红色框内&…

SwiftUI 6.0(iOS/iPadOS 18)中全新的 Tab 以及 Sidebar+悬浮 TabView 样式

概览 看来苹果一直对 iPadOS 中标签栏&#xff08;TabView&#xff09;不甚满意。这不&#xff0c;在 WWDC 2024 中苹果又对 TabView 外观做了大幅度的进化。 现在我们可以在顶部悬浮条和左侧的 Sidebar 两种不同布局之间恣意切换 TabView 的外观啦。而且&#xff0c;这在 Swi…

(超详细)基于动态顺序表实现简单的通讯录项目

前言&#xff1a; 我们在上一章节用c语言实现了线性表中的的动态顺序表&#xff0c;那么顺序表就只是顺序表吗&#xff1f;当然不是&#xff0c;使用顺序表结构可以实现很多项目&#xff0c;许多项目的数据结构都会用到顺序表&#xff0c;本章节我们就要使用顺序表实现一个简易…

腾讯云部署的java服务,访问阿里云的mysql数据库,带宽异常偏高,可能是什么原因

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

js语音识别,语音转文字,speech recognition(需要翻墙才能识别)

先上代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><meta name"viewport" content"widthdevice-width,initial-scale1.0"><title>test</title> </head><body><div id"…

人工智能的头号威胁:投毒攻击

随着掌管数字生活入口的万亿美元俱乐部企业——苹果公司跳入人工智能&#xff08;AI&#xff09;赛道&#xff0c;AI技术民主化的大幕正式拉开&#xff0c;同时也将AI安全问题推向舆论的风口浪尖。 根据瑞银本周一的智能手机调查报告&#xff0c;在中国以外的智能手机用户中&am…

Java多线程基础知识-2

线程的3个方法&#xff1a; Thread.sleep()&#xff1a;当前线程睡眠多少毫秒&#xff0c;让给其他线程去执行。 Thread.yield()&#xff1a;当前线程退出一下&#xff0c;进入到等待队列&#xff0c;让其他线程执行&#xff0c;即让出线程一下。 Thread.join()&#xff1a;…

《模拟联合国2.9—团队协作》

感谢上海财经大学持续的邀请&#xff0c;今天在阶梯教室举办的《模拟联合国2.0—团队协作》沙盘课程圆满结束。尽管场地的限制带来了一定的挑战&#xff0c;但得益于系统思考中“结构影响行为”的原则&#xff0c;我得以在不同场景中巧妙设计课程结构&#xff0c;极大地促进了大…

【JavaScript复习二】选择结构if和Switch(1)

### []( )2、单分支条件分支语句if (条件表达式) { // 条件为真时&#xff0c;做的事情 } else { // 条件为假时&#xff0c;做的事情 } ### []( )2,、多分支的 if 语句if (条件表达式1) { // 条件1为真时&#xff0c;做的事情} else if (条件表达式2) { // 条件1不满足&…

股票交易系统

效果展示&#xff0c;如下动图&#xff1a; 首先简述一下股票交易规则&#xff1a; 买卖股票&#xff0c;股民可以自行选择股票的买入或卖出价格和股票的数量&#xff0c;但是用户不一定马上就交易成功&#xff0c;只有当股票价格低于买入价才有机会买入&#xff0c;高于卖出价…

ccie在香港值钱吗?ccie认证很难考吗?

思科ccie认证可以算得上是网络高级工程师的一个标配证书&#xff0c;特别是在香港工作的朋友更是需要尽早拿下这个认证&#xff0c;它能让你的求职之路更为顺利。而已经入职的工程师为了不被时代所淘汰&#xff0c;也需要该证书保驾护航。 你知道ccie在香港值钱吗?ccie认证是不…

redis高可用-哨兵机制

一&#xff1a;背景 上一节我们已经实现了redis的主从同步&#xff0c;从而实现服务的流量分摊和数据高可用&#xff0c;但是出现故障以后&#xff0c;需要人工手动接入&#xff0c;手动切换主从&#xff0c;来实现故障转移。这是比较麻烦的&#xff0c;毕竟人不能实时盯着服务…

Covalent实现对1000亿笔链上交易解析,支持AI长期数据可用性

在区块链与人工智能&#xff08;AI&#xff09;交汇处&#xff0c;讨论往往集中于去中心化推理和去中心化训练等方面。然而&#xff0c;这一数据的关键组成部分却一直未得到足够的重视。一个主要问题是&#xff1a;我们如何保护 AI 模型中的数据不受偏见和操纵的影响&#xff1…

【计算机组成原理】指令系统考研真题详解之拓展操作码!

苏泽 “弃工从研”的路上很孤独&#xff0c;于是我记下了些许笔记相伴&#xff0c;希望能够帮助到大家 另外&#xff0c;利用了工作之余的一点点时间&#xff0c;整理了一套考研408的知识图谱&#xff0c; 我根据这一套知识图谱打造了这样一个408知识图谱问答系统 里面的每一…

C语言的网络编程

目录 引言 一、TCP/IP概述 1. TCP&#xff08;Transmission Control Protocol&#xff09; 2. UDP&#xff08;User Datagram Protocol&#xff09; 二、Socket编程基础 1. 服务器端 2. 客户端 三、URL与HTTP编程 1. 使用libcurl进行HTTP请求 表格总结 TCP/IP与Socke…

镭速传输界面优化之静态文件加载

镭速一直是众多企业传输大文件和大数据的优选对象&#xff0c;速度快、稳定且安全是市场上传输软件脱颖而出的立杆标签&#xff0c;那么同样在界面优化和体验的强大也能够给企业用户带来许多直观的感受&#xff0c;那么今天我们就来谈谈镭速是如何做到这些的&#xff0c;在界面…