吃瓜task03

第四章 决策树

4.1 基本流程

在这里插入图片描述

从逻辑角度就是 很多if else语句的组合
从集合角度,根据某种准则划分特征值空间
最终目的:将样本越分越细 且越来越纯(接近我们希望的划分的结果)

决策树的生成是一个递归过程:
当前属性集为空,或是所有样本在所有属性上取值同,无法划分

把当前结点标记为叶结点,并将其类别设定为该结点所含样本最多的类别

利用当前结点的后验分布

当前结点包含的样本集合为空,不能划分

把当前结点标记为叶结点,但将其类别设定为父结点所含样本做多的类别

把父结点的样本分布作为当前结点的先验分布

4.2划分选择

信息熵(information entropy)是度量样本集合纯度最常用的一种指标. 假定当前样本集合 D 中第 k 类样本所占的比例为 Pk (k = 1,2,. . . , |Y|),则 D 的信息熵定义为在这里插入图片描述

信息熵的三个性质:
单调性:发生概率越高的事件,其携带的信息量越低;
非负性:信息熵可以看作为一种广度量,非负性是一种合理的必然;
累加性:即多随机事件同时发生存在的总不确定性的量度是可以表示为各事件不确定性的量度的和,这也是广度量的一种体现。

信息增益
ID3决策树算法 使用信息增益(Information Gain)选择划分属性。
偏好:信息增益对可取数目较多的属性有所偏好。

属性a的信息增益公式定义为:
在这里插入图片描述
其中,D指样本集;a指属性;v 和V指a所有可能的取值;Dv指属性值为v vv时划分得到的子集。
信息增益越大,意味着由这个属性来进行划分对纯度的提升越大,即对决策的帮助越大。所以对每个属性均求出信息增益,再取最大的那个,就是最优划分属性。

信息增益最优属性公式定义:
在这里插入图片描述
增益率
如果一个属性对每个样本的取值都是不同的,那么针对这个属性的每个取值只包含一个样本并且分支结点的纯度已达最大,这样的决策树显然没有泛化能力,无法对新样本进行预测。因为新的样本在这个属性的值与决策树所学习的均不相同。因为信息增益准则对可取值数目较多的属性有偏好。所以增益率要对信息增益进行优化。
增益率公式定义:
在这里插入图片描述
在这里插入图片描述
基尼系数
属性a的基尼指数公式定义为:在这里插入图片描述
选基尼指数最小的那个属性作为最优化分属性:在这里插入图片描述

4.3剪枝处理

剪枝(pruning)是决策树学习算法对付过拟合的主要手段。
决策树剪枝的基本策略有预剪枝(prepruning)和后剪枝(post-pruning)。
预剪枝:在决策树生成过程中,对每个结点在划分前先进行估计,若当前结点的划分不能带来决策树泛化性能提升,则停止划分并将当前结点标记为叶结点;
预剪枝基于“贪心”本质禁止一些分支展开,给预剪枝决策树带来了欠拟合的风险。

后剪枝:先从训练集生成一棵完整的决策树,然后自底向上的对非叶结点进行考察,若将该结点对应的子树替换为叶结点能带来决策树泛化性能提升,则将该子树替换为叶结点。
后剪枝决策树通常比预剪枝决策树保留更多的分支。一般情形下,后剪枝决策树欠拟合的风险很小,泛化性能往往优于预剪枝决策树。但后剪枝过程是在生成完全决策树之后进行的,并且要自底向上的对树中所有非叶结点进行逐一考察,因此其训练时间开销比未剪枝和预剪枝决策树都要大得多。

4.4连续与缺失值

由于连续属性的可取值数目不再有限, 因此,不能直接根据连续属性的可 取值来对结点进行划分.此时,连续属性离散化技术可派上用场. 最简单的策 略是采用二分法(bi-partition)对连续属性进行处理,这正是 C4.5 决策树算法中 采用的机制

缺失值处理:不完整的样本(即样本的某些属性值缺失,如何在属性值缺失的情况下进行划分属性选择,给定划分属性,若样本在该属性上缺失,样本如何划分)—>让同一个样本以不同的概率划入到不同的子结点中去在这里插入图片描述

4.5多变量决策树

把样本放到坐标系中去考虑:
决策树形成的分类边界有一个明显的特点:轴平行,即他的分类边界和若干个与坐标轴平行的分段组成。

在这里插入图片描述

如果能用斜的边界来搞定,那么模型会得到简化:引出多变量决策树(MDT)

MDT中每个节点不再针对某个属性,而是属性的线性组合。
换言之,每个节点是一个线性分类器。
在这里插入图片描述

<think>我们正在讨论的是:在C#中如何保证当前async Task执行完毕(即等待异步任务完成)。 根据用户提供的引用内容,特别是引用[1]、[2][4]中关于异步编程(async/awaitTask)的说明,我们可以总结出几种等待异步任务执行完毕的方法。 关键点: 1. 使用`await`关键字:这是最直接的方式,在异步方法中使用`await`等待任务完成,这样会异步等待(不阻塞主线程)直到任务完成。 2. 使用`Task.Wait()`方法:这会阻塞当前线程直到任务完成。注意,在UI线程中使用可能导致死锁,因此需谨慎。 3. 使用`Task.Result`属性:这会阻塞当前线程并获取任务的结果,同样可能引起死锁。 4. 使用`Task.GetAwaiter().GetResult()`:与`Task.Wait()`类似,但可以提供更好的异常传播。 然而,在异步编程中,最佳实践是尽可能使用`await`,因为它不会阻塞线程,可以避免死锁,并且代码更清晰。 根据用户的问题,他想要“保证当前async Task执行完毕”,我们可以提供以下几种方法: 方法1:使用`await`(推荐) 在调用异步方法的地方使用`await`,这样当前方法会等待该任务完成,但不会阻塞线程(当前方法必须是异步方法)。 方法2:使用`Wait()`(阻塞调用,不推荐在UI线程使用) 如果你在同步方法中,并且确定不会造成死锁(比如在控制台应用程序中),可以使用`Wait()`方法。 方法3:使用`GetAwaiter().GetResult()`(与Wait类似,但异常处理不同) 同样是在同步方法中阻塞等待,可以获取到原始异常(而不是AggregateException)。 注意:在UI线程(如WinForms、WPF)或ASP.NET中,阻塞调用(如Wait(), Result)容易导致死锁。因此,在可能的情况下,应该使用`await`。 示例代码: 假设我们有一个异步方法: ```csharp public async Task DoSomethingAsync() { await Task.Delay(1000); // 模拟耗时操作 } ``` 我们需要保证这个任务执行完毕: 方式1(使用await): ```csharp public async Task MyMethodAsync() { await DoSomethingAsync(); // 后续代码将在DoSomethingAsync完成后执行 } ``` 方式2(使用Wait,在同步方法中): ```csharp public void MyMethod() { DoSomethingAsync().Wait(); // 阻塞当前线程直到任务完成 } ``` 方式3(使用GetAwaiter().GetResult()): ```csharp public void MyMethod() { DoSomethingAsync().GetAwaiter().GetResult(); } ``` 但是,在Unity中(根据引用[2]),我们通常使用`await`,因为Unity也支持异步编程。 另外,引用[4]中提到的例子中使用了`t.Wait()`(被注释掉了)`await`。它指出使用`await`会阻塞等待结果但不会阻塞主程序。 因此,总结如下: 在异步上下文中,使用`await`是最佳选择。在同步上下文中,如果必须等待,则可以使用`Wait()`或`GetAwaiter().GetResult()`,但要警惕死锁风险。 特别提醒:在Unity中,由于主线程是单线程,如果从主线程上使用`Wait()`可能会导致死锁(因为任务可能需要回到主线程,而主线程被阻塞了)。 因此,在Unity中,建议使用`await`来等待任务完成。 下面我们按照要求组织回答: </think>### C# 中保证 async Task 执行完毕的方法 在异步编程中,确保 `async Task` 执行完毕是避免并发问题资源泄漏的关键。以下是几种常用方法: --- ### ✅ 推荐方法 1:使用 `await`(异步等待) ```csharp async Task MainMethod() { Task asyncTask = DoAsyncWork(); // 启动异步任务 await asyncTask; // 等待任务完成 Debug.Log("任务已完成"); // 此处保证任务已结束 } ``` **原理**: `await` 会挂起当前方法,但不阻塞线程。任务完成后自动恢复执行后续代码[^1][^2]。 **适用场景**: 所有支持 `async/await` 的上下文(Unity、ASP.NET等) --- ### ✅ 方法 2:使用 `Task.Wait()`(同步阻塞) ```csharp void SyncMethod() { Task asyncTask = DoAsyncWork(); asyncTask.Wait(); // 阻塞当前线程直到任务完成 Debug.Log("任务已完成"); } ``` **注意**: - ❗ 会导致当前线程阻塞,可能引发死锁(尤其在UI线程) - 仅限控制台应用或后台线程使用[^4] **风险场景**: ```csharp // 错误示例:在UI线程调用可能导致死锁 asyncTask.Wait(); // 若任务需返回UI线程则死锁 ``` --- ### ✅ 方法 3:使用 `Task.Result`(同步获取结果) ```csharp void GetResult() { Task<string> asyncTask = GetDataAsync(); string result = asyncTask.Result; // 阻塞直到返回结果 Debug.Log($"结果:{result}"); } ``` **特点**: - 同时等待任务完成并获取返回值 - 与 `Wait()` 有相同的死锁风险[^3] --- ### ✅ 方法 4:组合 `WhenAll`(多任务等待) ```csharp async Task RunMultipleTasks() { Task task1 = DoWork1Async(); Task task2 = DoWork2Async(); await Task.WhenAll(task1, task2); // 等待所有任务完成 Debug.Log("所有任务已完成"); } ``` **优势**: 高效等待多个并行异步任务[^4] --- ### 特殊情况处理方法 #### 🔄 处理未捕获的异常 ```csharp async Task SafeWait() { Task riskyTask = RiskyOperationAsync(); try { await riskyTask; } catch (Exception ex) { Debug.LogError($"任务失败: {ex.Message}"); } } ``` #### ⏱️ 添加超时控制 ```csharp async Task WaitWithTimeout() { Task longTask = LongRunningTaskAsync(); if (await Task.WhenAny(longTask, Task.Delay(3000)) != longTask) { throw new TimeoutException("任务超时"); } } ``` --- ### 方法对比表 | 方法 | 等待方式 | 是否会阻塞线程 | 死锁风险 | 适用场景 | |------------------|------------|----------------|----------|-----------------------| | `await task` | 异步非阻塞 | ❌ | 无 | 所有异步上下文(推荐)| | `task.Wait()` | 同步阻塞 | ✅ | 高 | 控制台/后台线程 | | `task.Result` | 同步阻塞 | ✅ | 高 | 需立即获取结果 | | `Task.WhenAll()` | 异步非阻塞 | ❌ | 无 | 多任务并行等待 | --- ### 最佳实践建议 1. **优先使用 `await`** 避免阻塞线程,99% 场景应作为首选[^1][^2] 2. **避免在 UI 线程使用同步等待** `Wait()` `Result` 在 WPF/WinForms/Unity 的 UI 线程中极易引发死锁 3. **始终处理异常** 通过 `try-catch` 捕获异步任务中的异常 4. **谨慎使用 `async void`** 改为 `async Task` 以便调用方能等待任务完成[^2] > ⚠️ **关键原则**: > 异步调用栈中应始终保持 `async/await` 传递("async all the way") > 如必须在同步方法中等待,使用 `GetAwaiter().GetResult()` 优于 `Wait()`(提供更精确的异常堆栈) ```csharp // 同步方法中的相对安全做法 void SyncWrapper() { DoAsyncWork().GetAwaiter().GetResult(); } ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值