问题背景

虽说在MindSpore-2.3之后的版本中不在正式的发行版中支持GPU硬件后端,但其实在开发分支版本中对GPU后端是有支持的:

但是在安装的过程中可能会遇到一些问题或者报错,这里复现一下我的Ubuntu-20.04环境下的安装过程。

Pip安装

基本的安装流程是这样的,首先使用anaconda创建一个python-3.9的虚拟环境,因为在MindSpore-2.4版本之后不再支持python-3.7:

$ conda create -n mindspore-master python=3.9

然后根据自己的本地环境,执行相应的pip安装指令,例如:

$ python3 -m pip install mindspore-dev -i https://pypi.tuna.tsinghua.edu.cn/simple

如果pip安装期间出现超时的问题,重新执行一遍上述流程即可。安装之后,执行如下指令对安装好的MindSpore进行校验:

$ python -c "import mindspore;mindspore.set_context(device_target='GPU');mindspore.run_check()"

接下来就是处理各种问题的时刻。

version XXX not found

第一个可能出现的问题类型是各种编译工具版本不匹配的问题,例如:

$ python -c "import mindspore;mindspore.set_context(device_target='GPU');mindspore.run_check()"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/__init__.py", line 18, in <module>
    from mindspore.run_check import run_check
  File "/home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/run_check/__init__.py", line 17, in <module>
    from ._check_version import check_version_and_env_config
  File "/home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/run_check/_check_version.py", line 28, in <module>
    from mindspore._c_expression import MSContext, ms_ctx_param
ImportError: /home/dechin/anaconda3/envs/mindspore-master/bin/../lib/libstdc++.so.6: version `CXXABI_1.3.8' not found (required by /home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/_c_expression.cpython-39-x86_64-linux-gnu.so)

这种情况下就是找不到
CXXABI_1.3.8
这个软件版本。但是如果检查一下系统里面的软件版本:

$ strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep CXXABI
CXXABI_1.3
CXXABI_1.3.1
CXXABI_1.3.2
CXXABI_1.3.3
CXXABI_1.3.4
CXXABI_1.3.5
CXXABI_1.3.6
CXXABI_1.3.7
CXXABI_1.3.8
CXXABI_1.3.9
CXXABI_1.3.10
CXXABI_1.3.11
CXXABI_1.3.12
CXXABI_TM_1
CXXABI_FLOAT128

我们发现
CXXABI_1.3.8
是存在的,而之所以有这样的报错,是因为在anaconda创建的这个mindspore虚拟环境中不存在该版本:

$ strings /home/dechin/anaconda3/envs/mindspore-master/lib/libstdc++.so.6 | grep CXXABICXXABI_1.3
CXXABI_1.3.1
CXXABI_1.3.2
CXXABI_1.3.3
CXXABI_1.3.4
CXXABI_1.3.5
CXXABI_1.3.6
CXXABI_1.3.7
CXXABI_TM_1

那么解决的方案是这样的,我们可以直接把mindspore虚拟环境下的这个动态链接库做一个软连接,链接到系统库里面的对应动态链接库上:

$ ln -sf /usr/lib/x86_64-linux-gnu/libstdc++.so.6 /home/dechin/anaconda3/envs/mindspore-master/lib/libstdc++.so.6

再重新运行即可解决当前问题,类似的报错还有:

$ python3 -c "import mindspore;mindspore.set_context(device_target='GPU');mindspore.run_check()"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/__init__.py", line 18, in <module>
    from mindspore.run_check import run_check
  File "/home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/run_check/__init__.py", line 17, in <module>
    from ._check_version import check_version_and_env_config
  File "/home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/run_check/_check_version.py", line 28, in <module>
    from mindspore._c_expression import MSContext, ms_ctx_param
ImportError: /home/dechin/anaconda3/envs/mindspore-master/bin/../lib/libgomp.so.1: version `GOMP_4.0' not found (required by /home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/lib/libmindspore_backend.so)

也可以用相同的方法来处理。

cannot open shared object file

配置好上述环境之后,还有可能出现这样的报错信息:

$ python3 -c "import mindspore;mindspore.set_context(device_target='GPU');mindspore.run_check()"
[WARNING] ME(232647,7ff51906b4c0,python3):2024-11-18-09:54:31.123.673 [mindspore/ccsrc/runtime/hardware/device_context_manager.cc:65] GetNvccRealPath] Invalid environment variable CUDA_HOME [/home], can not find nvcc file [/home/bin/nvcc], please check the CUDA_HOME.
/home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/train/metrics/hausdorff_distance.py:20: UserWarning: A NumPy version >=1.22.4 and <2.3.0 is required for this version of SciPy (detected version 1.22.3)
  from scipy.ndimage import morphology
[ERROR] ME(232647:140690663584960,MainProcess):2024-11-18-09:54:32.148.524 [mindspore/run_check/_check_version.py:218] libcuda.so (need by mindspore-gpu) is not found. Please confirm that libmindspore_gpu.so is in directory:/home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/run_check/../lib/plugin and the correct cuda version has been installed, you can refer to the installation guidelines: https://www.mindspore.cn/install
[ERROR] ME(232647:140690663584960,MainProcess):2024-11-18-09:54:32.148.726 [mindspore/run_check/_check_version.py:218] libcudnn.so (need by mindspore-gpu) is not found. Please confirm that libmindspore_gpu.so is in directory:/home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/run_check/../lib/plugin and the correct cuda version has been installed, you can refer to the installation guidelines: https://www.mindspore.cn/install
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/_checkparam.py", line 1367, in wrapper
    return func(*args, **kwargs)
  File "/home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/context.py", line 1861, in set_context
    ctx.set_device_target(kwargs['device_target'])
  File "/home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/context.py", line 495, in set_device_target
    self.set_param(ms_ctx_param.device_target, target)
  File "/home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/context.py", line 187, in set_param
    self._context_handle.set_param(param, value)
RuntimeError: Unsupported device target GPU. This process only supports one of the ['CPU']. Please check whether the GPU environment is installed and configured correctly, and check whether current mindspore wheel package was built with "-e GPU". For details, please refer to "Device load error message".

----------------------------------------------------
- Device load error message:
----------------------------------------------------
Load dynamic library: libmindspore_ascend.so.2 failed. libge_runner.so: cannot open shared object file: No such file or directory
Load dynamic library: libmindspore_gpu.so.11.6 failed. libcublas.so.11: cannot open shared object file: No such file or directory
Load dynamic library: libmindspore_gpu.so.11.1 failed. libcublas.so.11: cannot open shared object file: No such file or directory
Load dynamic library: libmindspore_gpu.so.10.1 failed. libcudnn.so.7: cannot open shared object file: No such file or directory

----------------------------------------------------
- C++ Call Stack: (For framework developers)
----------------------------------------------------
mindspore/core/utils/ms_context.cc:287 SetDeviceTargetFromInner

这里的提示是找不到
libmindspore_gpu.so.11.6
等等动态链接库的地址。那么解决的方案是这样的,我们先去系统里面搜索一下这几个库,如果有存在相应的版本号,我们把所在位置的
lib
路径配置到
LD_LIBRARY_PATH
中即可:

$ sudo find / -name libcublas.so*
/home/dechin/anaconda3/envs/mindspore-latest/lib/libcublas.so
/home/dechin/anaconda3/envs/mindspore-latest/lib/libcublas.so.11.3.0.106
/home/dechin/anaconda3/envs/mindspore-latest/lib/libcublas.so.11
/home/dechin/anaconda3/envs/mindsponge/lib/libcublas.so
/home/dechin/anaconda3/envs/mindsponge/lib/libcublas.so.11.3.0.106
/home/dechin/anaconda3/envs/mindsponge/lib/libcublas.so.11
/home/dechin/anaconda3/envs/mindspore-master/lib/libcublas.so
/home/dechin/anaconda3/envs/mindspore-master/lib/libcublas.so.10
/home/dechin/anaconda3/envs/mindspore-master/lib/libcublas.so.10.2.2.89
/usr/lib/x86_64-linux-gnu/libcublas.so.10.2.1.243
/usr/lib/x86_64-linux-gnu/libcublas.so.10.1.0.105
/usr/lib/x86_64-linux-gnu/stubs/libcublas.so
/usr/lib/x86_64-linux-gnu/libcublas.so
/usr/lib/x86_64-linux-gnu/libcublas.so.10

这里我们发现在我们新建的
mindspore-master
环境中确实没有相应的动态链接库版本,但是反而是旧版的mindspore环境下有相应的这几个动态链接库,于是我的解决方案是把旧版的mindspore环境中的
lib
配置到环境变量中,即可解决该问题:

$ export LD_LIBRARY_PATH=/home/dechin/anaconda3/envs/mindspore-master/lib:/home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/lib:/home/dechin/anaconda3/envs/mindsponge/lib

再次运行测试:

$ python3 -c "import mindspore;mindspore.set_context(device_target='GPU');mindspore.run_check()"
[WARNING] ME(232736,7f562eca06c0,python3):2024-11-18-09:55:58.717.253 [mindspore/ccsrc/runtime/hardware/device_context_manager.cc:65] GetNvccRealPath] Invalid environment variable CUDA_HOME [/home], can not find nvcc file [/home/bin/nvcc], please check the CUDA_HOME.
/home/dechin/anaconda3/envs/mindspore-master/lib/python3.9/site-packages/mindspore/train/metrics/hausdorff_distance.py:20: UserWarning: A NumPy version >=1.22.4 and <2.3.0 is required for this version of SciPy (detected version 1.22.3)
  from scipy.ndimage import morphology
MindSpore version:  2.4.0.dev20241103
The result of multiplication calculation is correct, MindSpore has been installed on platform [GPU] successfully!

可以看到,虽然有一些告警信息,但是最终的运行结果是正确的,需要忽略告警信息的话可以运行:

$ export GLOG_v=4

来配置mindspore日志等级。

这里有个问题是,如果用户的环境中没有安装旧版本的MindSpore。那么我个人认为比较方便的一个方案是,如果系统环境中有其他的
libcublas
,例如Jax或者Torch等框架环境下也会有这些相关的软件版本,可以把他们的所在路径直接配置到环境变量中即可。如果什么环境都没有,那我的建议是先另建一个虚拟环境,安装一个旧版本的MindSpore,例如
mindspore-gpu-2.2
,确保成功安装后,再将这个旧版的lib路径配置到新版本下的环境变量中。

Unsupported device target GPU

如果在运行的过程中有出现
Unsupported device target GPU
的话,并且自动去索引Ascend后端的动态链接库,这种情况发生的原因是没有配置
CUDA_HOME
这个环境变量。应该是,新版本mindspore底层判断硬件平台的逻辑是通过获取环境变量来的,所以需要手动配置一个
CUDA_HOME
参数即可,例如:

$ export CUDA_HOME=/home

虽然这样随意配置有可能导致一些告警信息,但并不影响程序的正确运行结果。

总结概要

本文介绍了在Ubuntu-20.04系统下安装最新的MindSpore-2.4-for-GPU版本的方法,以及安装过程中有可能出现的一些问题。虽然在MindSpore的正式版本中已经不再支持GPU硬件后端,但是开发版本目前还是持续在支持的,并且其中包含了2.3和2.4版本的新特性,只是算子层面没有更新和优化。对于GPU后端的MindSpore用户来说,也算是一个好消息。

版权声明

本文首发链接为:
https://www.cnblogs.com/dechinphy/p/mindspore-2-4.html

作者ID:DechinPhy

更多原著文章:
https://www.cnblogs.com/dechinphy/

请博主喝咖啡:
https://www.cnblogs.com/dechinphy/gallery/image/379634.html

参考链接

  1. https://www.mindspore.cn/install/

为什么需要
[EnumeratorCancellation]

在使用 C# 编写异步迭代器时,您可能会遇到如下警告:

warning CS8425: 异步迭代器“TestConversationService.ChatStreamed(IReadOnlyList<ChatMessage>, ChatCompletionOptions, CancellationToken)”具有一个或多个类型为 "CancellationToken" 的参数,但它们都未用 "EnumeratorCancellation" 属性修饰,因此将不使用所生成的 "IAsyncEnumerable<>.GetAsyncEnumerator" 中的取消令牌参数。

看到这样的警告,您可能会困惑:
究竟需要在异步迭代器的方法参数上添加
[EnumeratorCancellation]
属性吗?如果不添加,会有什么区别?

让我们深入探讨一下这个问题,揭示其背后的真相。

正常调用时,[EnumeratorCancellation] 的影响

如果您只是简单地在异步迭代器方法中传递一个普通的
CancellationToken
,无论是否使用
[EnumeratorCancellation]
,方法的行为似乎并没有显著区别。例如:

public async IAsyncEnumerable<int> GenerateNumbersAsync(CancellationToken cancellationToken = default)
{
    for (int i = 0; i < 10; i++)
    {
        cancellationToken.ThrowIfCancellationRequested();
        yield return i;
        await Task.Delay(1000, cancellationToken);
    }
}

public async Task ConsumeNumbersAsync()
{
    CancellationTokenSource cts = new CancellationTokenSource();
    Task cancelTask = Task.Run(async () =>
    {
        await Task.Delay(3000);
        cts.Cancel();
    });

    try
    {
        await foreach (var number in GenerateNumbersAsync(cts.Token))
        {
            Console.WriteLine(number);
        }
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("枚举已被取消");
    }

    await cancelTask;
}

输出如下:

0
1
2
枚举已被取消

在上述代码中,即使没有使用
[EnumeratorCancellation]
,取消令牌
cts.Token
依然会生效,导致迭代过程被取消。这可能会让开发者误以为
[EnumeratorCancellation]
没有实际作用,进而引发更多的困惑。

揭开真相:生产者与消费者的职责分离

实际上,
[EnumeratorCancellation]
的核心作用在于
实现生产者与消费者的职责分离
。具体来说:

  • 生产者
    (即提供数据的异步迭代方法)专注于数据的生成和响应取消请求,不关心取消请求的来源或何时取消。

  • 消费者
    (即使用数据的部分)负责控制取消逻辑,独立地决定何时取消整个迭代过程。

通过这种设计,生产者不需要知道取消请求是由谁或何时发起的,简化了生产者的设计,同时赋予消费者更大的控制权。这不仅提高了代码的可维护性和可复用性,还避免了取消逻辑的混乱。

示例说明

下面通过一个示例,直观地展示
[EnumeratorCancellation]
如何实现职责分离。

1. 定义异步迭代器方法

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

public class DataProducer
{
    public async IAsyncEnumerable<int> ProduceData(
        [EnumeratorCancellation] CancellationToken cancellationToken = default)
    {
        int i = 0;
        while (true)
        {
            cancellationToken.ThrowIfCancellationRequested();
            Console.WriteLine($"[Iterator] 生成数字: {i}");
            yield return i++;
            await Task.Delay(1000, cancellationToken); // 模拟数据生成延迟
        }
	}
}

在这个
DataProducer
类中,
ProduceData
方法使用
[EnumeratorCancellation]
标注了
cancellationToken
参数。这意味着,当消费者通过
WithCancellation
传递取消令牌时,编译器会自动将该取消令牌传递给
ProduceData
方法的
cancellationToken
参数。

2. 定义消费者方法

using System;
using System.Threading;
using System.Threading.Tasks;

public class DataConsumer
{
	public async Task ConsumeDataAsync(IAsyncEnumerable<int> producer)
	{
		using CancellationTokenSource cts = new CancellationTokenSource();

		// 在5秒后发出取消请求
		_ = Task.Run(async () =>
		{
			await Task.Delay(5000);
			cts.Cancel();
			Console.WriteLine("[Trigger] 已发出取消请求");
		});

		try
		{
			// 通过 WithCancellation 传递取消令牌
			await foreach (var data in producer.WithCancellation(cts.Token))
			{
				Console.WriteLine($"[Consumer] 接收到数据: {data}");
			}
		}
		catch (OperationCanceledException)
		{
			Console.WriteLine("[Consumer] 数据接收已被取消");
		}
	}
}


DataConsumer
类中,
ConsumeDataAsync
方法创建了一个
CancellationTokenSource
,并在5秒后取消它。通过
WithCancellation
方法,将取消令牌传递给
ProduceData
方法。这样,消费者完全控制了取消逻辑,而生产者只需响应取消请求。

3. 执行示例

public class Program
{
	public static async Task Main(string[] args)
	{
		var producer = new DataProducer();
		var consumer = new DataConsumer();
		await consumer.ConsumeDataAsync(producer.ProduceData());
	}
}

预期输出:

[Iterator] 生成数字: 0
[Consumer] 接收到数据: 0
[Iterator] 生成数字: 1
[Consumer] 接收到数据: 1
[Iterator] 生成数字: 2
[Consumer] 接收到数据: 2
[Iterator] 生成数字: 3
[Consumer] 接收到数据: 3
[Iterator] 生成数字: 4
[Consumer] 接收到数据: 4
[Trigger] 已发出取消请求
[Consumer] 数据接收已被取消

在5秒后,取消请求被触发,迭代器检测到取消并抛出
OperationCanceledException
,导致迭代过程被中断。请注意DataConsumer在接收生产出来的数据
IAsyncEnumerable<int>
时,已经错过了在生产函数中传入
cancellationToken
的机会,但作为消费者,仍然可以通过
.WithCancellation
方法进行优雅取消。

这展示了生产者与消费者如何通过
WithCancellation

[EnumeratorCancellation]
实现职责分离,消费者能够独立地控制取消逻辑,而生产者只需响应取消请求。

CancellationToken 与 WithCancellation 同时作用时的行为

那么,如果在异步迭代器方法中同时传递了
CancellationToken
参数,并通过
WithCancellation
指定了不同的取消令牌,取消操作会听哪个的?还是都会监听?

结论是:两者都会生效
,只要其中任意一个取消令牌被触发,迭代器都会检测到取消请求并中断迭代过程。这取决于方法内部如何处理多个取消令牌。

示例演示

以下是一个详细的示例,展示当同时传递
CancellationToken
参数和使用不同的
WithCancellation
时的行为。

1. 定义异步迭代器方法

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

public class EnumeratorCancellationDemo
{
    // 异步迭代器方法,接受两个 CancellationToken
    public async IAsyncEnumerable<int> GenerateNumbersAsync(
        [EnumeratorCancellation] CancellationToken cancellationToken,
        CancellationToken externalCancellationToken = default)
    {
        int i = 0;
        try
        {
            while (true)
            {
                // 检查两个取消令牌
                cancellationToken.ThrowIfCancellationRequested();
                externalCancellationToken.ThrowIfCancellationRequested();

                Console.WriteLine($"[Iterator] 生成数字: {i}");
                yield return i++;

                // 模拟异步操作
                await Task.Delay(1000, cancellationToken);
            }
        }
        finally
        {
            Console.WriteLine("[Iterator] 迭代器已退出。");
        }
	}
}

2. 定义消费者方法

public class Program
{
	static async Task Main(string[] args)
	{
		Console.WriteLine("启动枚举取消示例...\n");

		var demo = new EnumeratorCancellationDemo();

		// 测试1: 先取消方法参数
		Console.WriteLine("=== 测试1: 先取消方法参数 ===\n");
		await TestCancellation(demo, cancelParamFirst: true);

		// 测试2: 先取消 WithCancellation
		Console.WriteLine("\n=== 测试2: 先取消 WithCancellation ===\n");
		await TestCancellation(demo, cancelParamFirst: false);

		Console.WriteLine("\n演示结束。");
		Console.ReadLine();
	}

	static async Task TestCancellation(EnumeratorCancellationDemo demo, bool cancelParamFirst)
	{
		using CancellationTokenSource ctsParam = new CancellationTokenSource();
		using CancellationTokenSource ctsWith = new CancellationTokenSource();

		if (cancelParamFirst)
		{
			// 第一个取消任务:3秒后取消 ctsParam
			_ = Task.Run(async () =>
			{
				await Task.Delay(3000);
				ctsParam.Cancel();
				Console.WriteLine("[Trigger] 已取消 ctsParam (方法参数)");
			});

			// 第二个取消任务:5秒后取消 ctsWith
			_ = Task.Run(async () =>
			{
				await Task.Delay(5000);
				ctsWith.Cancel();
				Console.WriteLine("[Trigger] 已取消 ctsWith (WithCancellation)");
			});
		}
		else
		{
			// 第一个取消任务:3秒后取消 ctsWith
			_ = Task.Run(async () =>
			{
				await Task.Delay(3000);
				ctsWith.Cancel();
				Console.WriteLine("[Trigger] 已取消 ctsWith (WithCancellation)");
			});

			// 第二个取消任务:5秒后取消 ctsParam
			_ = Task.Run(async () =>
			{
				await Task.Delay(5000);
				ctsParam.Cancel();
				Console.WriteLine("[Trigger] 已取消 ctsParam (方法参数)");
			});
		}

		try
		{
			// 传递 ctsWith.Token 作为方法参数,并通过 WithCancellation 传递 ctsWith.Token
			await foreach (var number in demo.GenerateNumbersAsync(ctsWith.Token, ctsParam.Token).WithCancellation(ctsWith.Token))
			{
				Console.WriteLine($"[Consumer] 接收到数字: {number}");
			}
		}
		catch (OperationCanceledException ex)
		{
			string reason = ex.CancellationToken == ctsWith.Token ? "WithCancellation" : "方法参数";
			Console.WriteLine($"[Iterator] 迭代器检测到取消。原因: {reason}");
			Console.WriteLine("[Consumer] 枚举已被取消。");
		}
	}
}

3. 运行示例并观察结果

启动程序后,控制台输出可能如下所示:

启动枚举取消示例...

=== 测试1: 先取消方法参数 ===

[Iterator] 生成数字: 0
[Consumer] 接收到数字: 0
[Iterator] 生成数字: 1
[Consumer] 接收到数字: 1
[Iterator] 生成数字: 2
[Consumer] 接收到数字: 2
[Trigger] 已取消 ctsParam (方法参数)
[Iterator] 迭代器已退出。
[Iterator] 迭代器检测到取消。原因: 方法参数
[Consumer] 枚举已被取消。

=== 测试2: 先取消 WithCancellation ===

[Iterator] 生成数字: 0
[Consumer] 接收到数字: 0
[Iterator] 生成数字: 1
[Consumer] 接收到数字: 1
[Trigger] 已取消 ctsWith (WithCancellation)
[Iterator] 生成数字: 2
[Consumer] 接收到数字: 2
[Trigger] 已取消 ctsWith (WithCancellation)
[Iterator] 迭代器已退出。
[Iterator] 迭代器检测到取消。原因: WithCancellation
[Consumer] 枚举已被取消。

演示结束。

解释:

  1. 测试1:先取消方法参数 (
    ctsParam
    )


    • 在第3秒时,
      ctsParam
      被取消。
    • 迭代器检测到
      externalCancellationToken
      被取消,抛出
      OperationCanceledException
    • 终止迭代过程,即使
      ctsWith
      还未被取消。
  2. 测试2:先取消
    WithCancellation
    (
    ctsWith
    )


    • 在第3秒时,
      ctsWith
      被取消。
    • 迭代器检测到
      cancellationToken
      被取消,抛出
      OperationCanceledException
    • 终止迭代过程,即使
      ctsParam
      还未被取消。

关键点:

  • 独立生效
    :无论是通过方法参数传递的
    CancellationToken
    还是通过
    WithCancellation
    传递的
    CancellationToken
    ,只要其中一个被取消,迭代器就会响应取消请求并终止迭代。

  • 取消顺序无关紧要
    :不论先取消哪一个取消令牌,迭代器都会正确响应取消请求。取消操作的顺序不会影响最终的效果。

总结

通过上述示例,我们深入了解了
[EnumeratorCancellation]
的必要性及其在异步迭代器中的核心作用。简要回顾:

  • 消除警告
    :使用
    [EnumeratorCancellation]
    可以消除 Visual Studio 提示的警告,确保取消请求能够正确传递给异步迭代器方法。

  • 职责分离
    :它实现了生产者与消费者的职责分离,使生产者专注于数据生成,消费者控制取消逻辑,从而提升代码的可维护性和可复用性。

  • 灵活的取消机制
    :即使同时传递多个取消令牌,只要任意一个被取消,迭代器就会终止,提供了灵活而强大的取消控制能力。

.NET 的这些强大功能为开发者提供了极大的便利和灵活性,使得编写高效、可维护的异步代码变得更加轻松与自信。让我们为 .NET 的强大功能自豪,并在实际开发中善加利用这些工具,构建出更优秀的软件解决方案!

热点随笔:

·
开发人员,千万不要去碰那该死的业务参数,无论什么时候!
(
why技术
)
·
.NET 9正式发布,亮点是.NET Aspire和AI
(
张善友
)
·
AI之旅:起步即迈出重要一步,博客园与 Chat2DB 达成战略合作
(
博客园团队
)
·
瞧瞧别人的Controller,那叫一个优雅!
(
苏三说技术
)
·
在.NET 9中使用Scalar替代Swagger
(
马行空的博客
)
·
太强了!14.7K star!Windows系统居然还有开源替代版了?!
(
狂师
)
·
.NET现在可以做什么,有哪些公司在用的?
(
追逐时光者
)
·
程序员的悲哀是什么?
(
悟空非空也
)
·
使用 C# 入门深度学习:线性代数
(
痴者工良
)
·
基于.NET开源、功能强大且灵活的工作流引擎框架
(
追逐时光者
)
·
.NET 各版本贡献者列表
(
张善友
)
·
程序员如何借势AI提高自己:从高效工作到技能升级的全面指南
(
努力的小雨
)

热点新闻:

·
红旗不藏了:5nm车载芯片,纯视觉端到端智驾,固态电池也安排了
·
.NET 9 正式发布:性能大幅提升,提供 18 个月标准期限支持
·
李彦宏驳大模型泡沫论:文心日调用量15亿 智能体全面爆发
·
日产全球裁员9000人!拥抱华为自救,中国市场三年内推8款新能源车
·
比亚迪方程豹汽车豹 8 发布:首搭华为 HiCar,37.98 万元起
·
雷军:我马上去催一下 网友:“客服001号小雷为您服务”
·
NASA多次求借后 我国提议交换月球样本:美国至今沉默!
·
我国首个百万千瓦级海上光伏项目并网,项目用海面积约 1223 公顷
·
iPhone 部分旧款机型降至半价
·
雅迪上半年销量减少182万台,员工降薪,老板大幅加薪?
·
国产手机,买不起了
·
太荒谬了!千人公司一刀切禁用 JetBrains,非俄籍“备胎” VSCode 上位

前一篇:《探索训练人工智能模型的词汇大小与模型的维度》

序言:
Dropout 是神经网络设计领域的一种技术,通常我们把它翻译成 随机失活 或者 丢弃法。如果训练神经网络的时候不用 Dropout,模型就很容易“读死书”,也就是过拟合,结果可能导致项目失败。

那 Dropout 到底在干什么呢?其实很简单,就是在训练模型的时候,随机关掉隐藏层中的一些神经元,不让它们输出结果。没什么玄乎的,就是这么直接。比如说,在每一轮(epoch)训练中,会随机挑一些神经元“闭麦”,让它们暂时休息,输出值设为 0。但需要注意的是,哪些神经元会被关掉是随机的,每次都不一样,而不是每次关掉一批固定的神经元。这样操作的好处是,模型必须依赖所有神经元协同工作,去学习更普遍的规律,而不是只死记硬背几个特定特征。所以 Dropout 能很好地解决模型的“读死书”问题,让它更灵活、更聪明,也更有能力去识别它从未见过的新知识。

使用 Dropout

在减少过拟合方面,一个常用的技巧是在全连接神经网络中加入 Dropout。我们在第 3 章中探讨了它在卷积神经网络中的应用。这时可能会很想直接使用 Dropout 来看看它对过拟合的效果,但在这里我选择先不急着用,而是等到词汇表大小、嵌入维度和架构复杂度都调整好之后再试。毕竟,这些调整往往比使用 Dropout 对模型效果的影响更大,而我们已经从这些调整中看到了不错的结果。

现在,我们的架构已经简化到中间的全连接层只有 8 个神经元了,因此 Dropout 的作用可能会被最小化,但我们还是来试一试吧。以下是更新后的模型代码,加入了 0.25 的 Dropout(这相当于我们 8 个神经元中丢弃了 2 个):

model = tf.keras.Sequential([

tf.keras.layers.Embedding(vocab_size, embedding_dim),

tf.keras.layers.GlobalAveragePooling1D(),

tf.keras.layers.Dense(8, activation='relu'),

tf.keras.layers.Dropout(0.25),

tf.keras.layers.Dense(1, activation='sigmoid')

])

图 6-14 显示了训练 100 个周期后的准确率结果。这次我们看到训练集的准确率开始超过之前的阈值,而验证集的准确率则在慢慢下降。这表明我们又进入了过拟合的区域。

这一点通过图 6-15 的损失曲线得到了验证。


图 6-14:加入 Dropout 后的准确率


图 6-15:加入 Dropout 后的损失

从这里你可以看到,模型的验证损失又开始呈现出之前那种随着时间增加的趋势。虽然情况没有之前那么糟糕,但显然方向是不对的。

在这种情况下,由于神经元的数量非常少,加入 Dropout 可能并不是一个合适的选择。不过,Dropout 仍然是一个很好的工具,要记得把它放进你的工具箱,在比这个更复杂的架构中使用它。

总结:本节示例演示了在网络中引入 Dropout 的效果。从实验中我们可以看到,Dropout 是一个有效的工具,但它的作用依赖于模型架构和具体场景。对于像本例中这种简化的模型,Dropout 的影响较小。但在更复杂的模型中,它往往是防止过拟合的关键手段。接下来,我们还会介绍几种优化技术,帮助进一步解决模型过拟合的“读死书”问题

几年前MySQL数据库出现突然的从库延迟故障和CPU爆高时,如何排查具体原因,可能说已在腾讯云的MySQL库里开启了SQL全审计,记录了全部执行的SQL,再通过下面的方法就可以很容易找到原因:

1,
实用QPS和TPS高的高效分析方法

但如果没有开通SQL全审计,如何去找原因。当然说可以看慢SQL日志,但这时的慢SQL日志记录的SQL不全,就不好判断分析出具体是哪些SQL引起的。

如何在非开通SQL全审计的MySQL数据库(因实例不可能都开SQL审计,自建MySQL开还不方便存储查看等),
有什么办法?
有更多

信息来分析出原因!

当时接手公司数据库运维时,研发已经写了一个Shell,每10秒保存一下当前的:show full processlist ,保留当前执行SQL情况到文件中,后来发现这些还不够,后来加上长事务和表锁,可能更好,Shell脚本如下:

1,CPU爆高:

监控shell:

每10秒执行一次保留当前查询的监控到txt文本中

#!/bin/bash
cd $(
dirname $0)echo$log_file
sql
="show full processlist"ip=172.16.0.15start_time=$(date +%s)
let
"check_time=start_time+60";
now
=$start_timewhile [ $now -lt $check_time ];dolog_dir=/root/monitor/$(date +"%Y-%m-%d")/$(date +"%H")/$(date +"%M")mkdir -p $log_dir
log_file
=$log_dir/$(date +"%Y-%m-%d-%H-%M-%S")echo $sql | mysql -udba_op -ppwd -h$ip mysql >$log_file.master
mysql
-udba_op -ppwd -h$ip mysql </root/shell/long_tran.sql >$log_file.master_tranecho "next" sleep 10;
now
=$(date +%s)done

long_tran.sql下的SQL脚本:

select timestampdiff(SECOND,trx_started,now()) diffsecond,b.host,b.user,concat('kill',b.ID,';') killsql,b.db,
IFNULL(b.INFO,a.trx_query) runsql
from information_schema.innodb_trx a left joininformation_schema.PROCESSLIST bon a.trx_mysql_thread_id=b.IDwhere timestampdiff(SECOND,trx_started,now()) >=1 order by 1 desc limit 20;

show
open tables where in_use >=1;

Crontab运行调度和清理10天前的日志

*/1 * * * *  sh /root/shell/sg_monitor_master.sh > /dev/null 2>&1
1 1 * * *  find /root/monitor/ -name "20*" -type d -mtime +10 -exec rm -rf {}  \; > /dev/null 2>&1

这样每隔10秒就有下面2个文件:

可以结合慢SQL和这2个文件,监控出出问题时当前的数据库的SQL执行情况,找出问题。

2,从库突然延迟

因为当时公司是做在线教育的,但从库延迟几十秒,对他们影响很大,同时领导就会收到报警短信,钉钉群里叫,就要查是什么原因导致的从库延迟,一种方法是查看上面的监控处理的SQL获取信息,另外当时还有解析出binlog信息,查出具体的SQL,看是什么SQL导致的,具体做法:

1,从腾讯云的下载出问题时间的binlog日志本地的机器

2,根据出问题的时间范围,解析binlog日志,如下:

mysqlbinlog --no-defaults -vv --base64-output=decode-rows --start-datetime='2020-08-18 11:20:00' --stop-datetime='2020-08-18 12:03:00' mysql-bin.000203 >log.sql

3,解析出binlog日志后,用notepad++打开文件,仔细查看运行的SQL日志,分析出SQL,这就是细心活。如果查到具体表,可以用上
grep
过滤,如下:

1, grep user_0  -A 10 -B 10 >/data/61.txt
2, mysqlbinlog --start-datetime="2019-07-08 9:32:00" --stop-datetime="2019-07-09 11:20:00" -v --base64-output=DECODE-ROWS binlog.000001 | grep -A4 'INSERT' > test.sql

4,结合上面信息,发出SQL,让研发同学去修改业务。

注意binlog:
1,主库开启gtid后,pos位置点,每个binlog日志文件开始的pos都是从1开始计算
2,binlog的replace,会转成对应的idelete和insert,或者update等
3,binlog: 执行的SQL都是带有库名

--base64-output参数用来控制binlog部分是否显示出来的,指定为decode-rows表示不显示binglog部分
加-v与加-vv的区别
加-vv
### UPDATE `online`.`ol_teacher_inversion_rate_list` ### WHERE ### @1=595135615 /* INT meta=0 nullable=0 is_null=0 */
加-v
### UPDATE `online`.`ol_teacher_inversion_rate_list` ### WHERE ### @1=595135615
少一个v,少后面的字段类型信息
加--base64-output=DECODE-ROWS与不加的区别:
加:--base64-output=DECODE-ROWS
不显示原始binlog的二进制信息 :如 BINLOG ' JV7gWg8QJwAAdwAA.......'
4,binlog批量删除

如:DELETE FROM `online`.`ol_task_agents_fixed` WHERE `record_date` = '2021-04-21'
在binlog会一条一条记录,删除5000条,会有5000条delete语句,而且是同一个事务。

3,记录原始SQL

当时在公司,公司技术VP说要记录执行的原始SQL,这样大数据读取binlog,把这个原始SQL保留起来,日后审计时就可以通过原始SQL发现谁乱改线上业务数据,同时要把研发人员每次业务要改数据的SQL邮件抄送给他。查询发现mysql还有这样的参数:
binlog_rows_query_log_events参数

默认配置下,ROW格式二进制日志只记录数据发生的变化,并不会记录什么语句导致数据发生变化,而出于审计或者处理bug的需求,需要了解导致数据变化的SQL语句,MYSQL提供了binlog_rows_query_log_events来控制是否在二进制中存放"原始SQL"。开启后如下:

后来在主库上开启该参数,大数据就记录主库的SQL执行情况,要监控谁有乱修改更新线上业务数据,但后面一直没用这个数据去查,反而大数据说记录的执行SQL数据量太大,不得不缩短数据保留日期。