08
6
原文:bit.ly/3wFqDy9
作者:Daniel
译者:王亮

.NET 6 预览版 4 现已发布,其中包括对 ASP.NET Core 的许多新改进。

下面是此次预览版中 ASP.NET Core 的更新内容:

1 开始使用

要开始使用 .NET 6 Preview 4 中的 ASP.NET Core,请安装 .NET 6 SDK[1]。

如果你在 Windows 上使用 Visual Studio,我们建议安装 Visual Studio 2019 16.11 的最新预览版。如果你在 macOS 上,我们建议安装 Visual Studio 2019 for Mac 8.10 的最新预览版。

2 升级一个现有的项目

要将一个现有的 ASP.NET Core 应用程序从 .NET 6 Preview 3 升级到.NET 6 Preview 4。

将所有 Microsoft.AspNetCore. 引用包更新为 6.0.0-preview.4.

更新所有 Microsoft.Extensions. 引用包更新为 6.0.0-preview.4.

请参阅 .NET 6 中 ASP.NET Core 的完整中断变化列表[2]。

3 引入最小 API

在 .NET 6 中,我们为 Web 应用的托管和路由引入了最小 API。这为使用 .NET 构建第一个 Web 应用程序以及想要构建小型微服务和 HTTP API 的开发者打开了大门。这些精简的 API 提供了 ASP.NET MVC 的优点。

要尝试创建一个最小 API,请创建一个新的 ASP.NET Core 空 Web 应用。

dotnet new web -o MinApi

只需一个文件和几行代码,你现在就有一个功能齐全的 HTTP API。

file

4 新的路由 API

新的路由 API 允许用户路由到任何类型的方法。这些方法可以使用类似控制器的参数绑定、JSON 格式化和 Action 结果。

之前(使用现有的 Map APIs):

app.MapGet("/", async httpContext =>
{
    await httpContext.Response.WriteAsync("Hello World!");
});

现在(使用新的 Map 重载):

app.MapGet("/", (Func<string>)(() => "Hello World!"));

5 C# 10 的改进

这些 API 已经利用了较新的 C# 特性,如顶层语句。在今年晚些时候与 .NET 6 一起发布的 C# 10 中,体验将变得更好。例如,不再需要明确地把类型转换成 (Func)。下面的图片展示了 C# 10 支持的特性:

file

开发者从使用类和方法到使用 lambda,拥有和使用 MVC 控制器及属性操作一样的功能。

6 新的托管(hosting) API

新的空 Web 模板使用的是 .NET 6 Preview 4 中引入的新的托管模式。

var app = WebApplication.Create(args);

app.MapGet("/", (Func<string>)(() => "Hello World!"));

app.Run();

你并不局限于只使用新的路由 API。下面是一个 Web 应用程序的例子,它被更新为使用新的托管模式,配置服务和添加中间件。

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Api", Version = "v1" });
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseSwagger();
    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Api v1"));
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

新的托管 API 减少了配置和启动 ASP.NET 应用程序所需的模板数量。

7 性能

这些新的路由 API 的开销比基于控制器的 API 少得多。使用新的路由 API,ASP.NET Core 能够在 TechEmpower[3] JSON 基准测试中达到约 80 万 RPS,而 MVC 则达到约 50 万 RPS。

file

异步流

ASP.NET Core 现在支持从控制器 Action 一直到响应的 JSON 格式化器的异步流。从 Action 中返回 IAsyncEnumerable,在发送之前不再在内存中缓冲响应内容。这有助于在返回可异步枚举的大型数据集时减少内存使用。

请注意,Entity Framework Core 提供了用于查询数据库的 IAsyncEnumerable 的实现。在 .NET 6 中,ASP.NET Core 对 IAsyncEnumerable 的支持有所改进,可以使 EF Core 与 ASP.NET Core 的使用更加高效。例如,下面的代码在发送响应前将不再把 Products 数据缓冲到内存中:

public IActionResult GetProducts()
{
    return Ok(dbContext.Products);
}

然而,如果你已经将 EF Core 设置为使用懒加载,这种新的行为可能会导致在数据被枚举时由于并发的查询执行而产生错误。你可以通过自己缓冲数据来恢复到以前的行为:

public async Task<IActionResult> Products()
{
    return Ok(await dbContext.Products.ToListAsync());
}

有关这一行为变化的更多细节,见相关公告[4]。

8 HTTP 日志中间件

HttpLogging 是一个新的内置中间件,可以记录 HTTP 请求和 HTTP 响应的信息,包括头信息和整个 Body。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseHttpLogging();
}

HttpLogging 中间件提供了以下日志:

HTTP 请求信息

普通属性

头信息

请求 Body

HTTP 响应信息

为了配置 HTTP 日志中间件,你可以在对 ConfigureServices() 的调用中指定 HttpLoggingOptions:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpLogging(logging =>
    {
        // Customize HTTP logging here.
        logging.LoggingFields = HttpLoggingFields.All;
        logging.RequestHeaders.Add("My-Request-Header");
        logging.ResponseHeaders.Add("My-Response-Header");
        logging.MediaTypeOptions.AddText("application/javascript");
        logging.RequestBodyLogLimit = 4096;
        logging.ResponseBodyLogLimit = 4096;
    });
}

这会在日志中产生新的带有 Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware 类别的 HTTP 请求信息。

关于如何使用 HTTP 日志的更多信息,请看 HTTP 日志文档[5]。

9 使用 Kestrel 作为默认启动

file

对于在 .NET 6 Preview 4 中创建的所有新项目,我们已将默认的启动配置文件从 IIS Express 改为 Kestrel。在开发应用程序时,启动 Kestrel 的速度明显加快,并带来了更灵敏的体验。

IIS Express (ms) Kestrel (ms) % change
Debugging 4359 2772 36%
No debugging 1999 727 64%

IIS Express 仍然可以作为启动项,用于 Windows 认证或端口共享等情况。

10 IConnectionSocketFeature

IConnectionSocketFeature 功能使你能够访问与当前请求相关的底层接受 socket。它可以通过 HttpContext 上的 FeatureCollection 访问。

例如,下面的应用程序在接受 socket 上设置 LingerState 属性:

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureEndpointDefaults(listenOptions => listenOptions.Use((connection, next) =>
    {
        var socketFeature = connection.Features.Get<IConnectionSocketFeature>();
        socketFeature.Socket.LingerState = new LingerOption(true, seconds: 10);
        return next();
    }));
});
var app = builder.Build();
app.MapGet("/", (Func<string>)(() => "Hello world"));
await app.RunAsync();

11 改进单页应用(SPA)模板

略...(译注:文字太长,懒得翻译了,主要 VS 中的 SPA 模板我从来不用)

12.NET 热重载

最新的 Visual Studio 预览版对.NET Hot 热重载有一些初步的支持。你可能已经注意到在调试你的应用程序时,新的 Apply Code Changes 按钮和调试选项。

Apply Code Changes 按钮将用你所作的代码修改来更新正在运行的应用程序,甚至不需要保存。下面是一个更新 Counter 组件的例子,它的增量从 1 改为 2。请注意,一旦修改被应用,当前的计数不会丢失:

file

Visual Studio 中的 .NET 热重载支持仍在进行中,因此在 ASP.NET Core 应用程序中使用它时有一些限制:

你必须在连接调试器的情况下运行以应用更改。

代码修改只能应用于 C# 文件--还不支持对 Razor 文件(.razor, .cshtml)的修改。

已应用的更改还不能强制更新用户界面,因此需要手动触发用户界面更新。

目前不支持 Blazor WebAssembly 应用程序。

所有这些限制都在解决中,并将在未来的 Visual Studio 更新中得到解决。敬请关注!

如果你通过 dotnet watch 使用 .NET 热重载,修改将被应用于 ASP.NET Core 托管的 Blazor WebAssembly 应用程序。如果你刷新浏览器,修改也会重新应用到你的 Blazor WebAssembly 应用程序。

要了解更多关于.NET 热重载的信息,你可以在我们的博文中获得所有细节:介绍.NET 热重载[6]。

13 Razor 中的泛型约束

在 Razor 中使用 @typeparam 指令定义通用类型参数时,你现在可以使用标准的 C# 语法指定泛型约束。

@typeparam TEntity where TEntity : IEntity

14 Blazor 错误边界

Blazor 错误边界提供了一种方便的方式来处理组件层次结构中的异常情况。为了定义一个错误边界,使用新的 ErrorBoundary 组件来包裹一些现有的内容。只要一切运行顺利,ErrorBoundary 组件将渲染其子内容。如果一个未处理的异常被抛出,ErrorBoundary 会渲染一些错误 UI。

例如,我们可以像这样在默认 Blazor 应用程序的布局中添加一个错误边界:

<div class="main">
  <div class="top-row px-4">
    <a href="https://docs.microsoft.com/aspnet/" target="_blank" rel="noopener">
      About
    </a>
  </div>

  <div class="content px-4">
    <ErrorBoundary> @Body </ErrorBoundary>
  </div>
</div>

该应用程序继续像以前一样运作,但现在我们的错误边界将处理未处理的异常。例如,我们可以更新 Counter 组件,在计数过大时抛出一个异常。


private void IncrementCount()
{
    currentCount++;
    if (currentCount > 10)
    {
        throw new InvalidOperationException("Current count is too big!");
    }
}

现在,如果我们过多地点击计数器,就会抛出一个未处理的异常,这将由我们的错误边界通过渲染一些默认的错误界面来处理。

file

默认情况下,ErrorBoundary 组件为其错误内容渲染了一个带有 blazor-error-boundary CSS 类的空 div。这个默认界面的颜色、文本和图标都是在应用程序中使用 CSS 定义的,所以你可以自由地定制它们。你也可以通过设置 ErrorContent 属性来改变默认的错误内容。

<ErrorBoundary>
  <ChildContent> @Body </ChildContent>
  <ErrorContent>
    <p class="my-error">Nothing to see here right now. Sorry!</p>
  </ErrorContent>
</ErrorBoundary>

因为我们在布局中定义了错误边界,一旦抛出一个异常,不管我们导航到哪个页面我们都能看到错误内容。一般来说,错误边界的范围最好比这更窄,但我们可以选择在随后的页面导航中通过调用错误边界的恢复方法将错误边界重置为非错误状态。

<ErrorBoundary @ref="errorBoundary">
    @Body
</ErrorBoundary>
...

@code {
    ErrorBoundary errorBoundary;

    protected override void OnParametersSet()
    {
        // On each page navigation, reset any error state
        errorBoundary?.Recover();
    }
}

15 Blazor WebAssembly 的 AOT 编译

Blazor WebAssembly 现在支持 AOT(ahead-of-time) 编译,你可以将你的 .NET 代码直接编译为 WebAssembly,以显著提高运行时性能。现在 Blazor WebAssemby 应用程序使用 WebAssembly 中实现的 .NET IL 解释器运行。由于 .NET 代码是被解释的,通常这意味着在 WebAssembly 上运行的 .NET 代码比在正常的.NET 运行时要慢得多。.NET WebAssembly AOT 编译通过将你的 .NET 代码直接编译成 WebAssembly 来解决这个性能问题。

AOT 编译你的 Blazor WebAssembly 应用程序,对于 CPU 密集型任务来说,其性能的提高是相当显著的。例如,下面的片段显示了使用相同的 Blazor WebAssembly 应用程序进行一些基本图像编辑的比较,首先使用解释器,然后是 AOT 编译。AOT 编译后的版本运行速度快了五倍以上。

file

你可以在 GitHub 上查看这个 PictureFixer[7] 的代码。

.NET WebAssembly AOT 编译需要一个额外的构建工具,必须作为一个可选的 .NET SDK 工作负载来安装才能使用。要安装 .NET WebAssembly 构建工具,请运行以下命令:

dotnet workload install microsoft-net-sdk-blazorwebassembly-aot

为了在你的 Blazor WebAssembly 项目中启用 WebAssembly AOT 编译,在你的项目文件中添加以下属性:

true

然后 AOT 将你的应用程序编译成 WebAssembly,发布应用程序。使用 Release 配置发布将确保 .NET IL 链接也被运行,以减少发布应用程序的大小。

dotnet publish -c Release

WebAssembly AOT 编译只在项目发布时进行。当项目在开发过程中运行时,它并不使用。这是因为 WebAssembly AOT 编译可能需要一段时间。

AOT 编译的 Blazor WebAssembly 应用程序的大小通常比作为 .NET IL 的应用程序要大。在我们的测试中,大多数 AOT 编译的 Blazor WebAssembly 应用程序大约大2倍,不过这取决于具体的应用程序。这意味着,使用 WebAssembly 的 AOT 编译,可以用加载时间的性能换取运行时间的性能。这种权衡是否值得,取决于你的应用程序。

16 .NET MAUI Blazor 应用

Blazor 能够用 .NET 构建客户端 Web UI,但有时你需要的东西比 Web 平台提供的更多。有时你需要完全访问设备的本地功能。现在,你可以在 .NET MAUI 应用程序中托管 Blazor 组件,以使用 Web UI 构建跨平台的本地应用程序。这些组件在.NET 进程中原生运行,并使用本地互操作通道向嵌入式 Web 视图控件渲染 Web UI。这种混合方法为你提供了本地和网络的优点。你的组件可以通过 .NET 平台访问本地功能,并呈现标准的 Web UI。.NET MAUI Blazor 应用程序可以运行在任何 .NET MAUI 可以运行的地方(Windows、Mac、iOS 和 Android),尽管我们对 .NET 6的主要关注是在桌面场景。

要创建一个 .NET MAUI Blazor 应用程序,你首先需要在开发机器上配置 .NET MAUI。最简单的方法是使用 maui-check 工具。要安装 maui-check 工具,请运行:

dotnet tool install -g Redth.Net.Maui.Check
然后运行 maui-check 来获取 .NET MAUI 工具和依赖。有关开始使用 .NET MAUI 的其他信息,请参考 GitHub 上的 wiki 文档。

一旦一切安装完毕,使用新的项目模板创建一个 .NET MAUI Blazor 应用程序:

dotnet new maui-blazor -o MauiBlazorApp
你也可以使用 Visual Studio 创建一个 .NET MAUI Blazor 应用程序:

file

.NET MAUI Blazor 应用程序是 .NET MAUI 应用程序,它使用 BlazorWebView 控件将 Blazor 组件渲染到一个嵌入式 Web 视图中。应用程序的代码和逻辑位于 MauiApp 项目中,该项目被设置为多目标 Android、iOS 和 Mac Catalyst。MauiApp.WinUI3 项目用于为 Windows 构建,而 MauiApp.WinUI3(Package) 项目则用于为 Windows 生成 MSIX 包。最终,我们希望将对 Windows 的支持合并到主应用程序项目中,但现在这些独立的项目是必要的。

在 MauiApp 项目的 MainPage.xaml 中设置了 BlazorWebView 控件:

<b:BlazorWebView HostPage="wwwroot/index.html">
    <b:BlazorWebView.RootComponents>
        <b:RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
    </b:BlazorWebView.RootComponents>
</b:BlazorWebView>

该应用程序的根 Blazor 组件在 Main.razor 中。其余的 Blazor 组件都在 Pages 和 Shared 目录下。请注意,这些组件与默认 Blazor 模板中使用的组件相同。你可以在你的应用程序中使用现有的 Blazor 组件,而不用改变代码,或者引用包含这些组件的现有类库或包。应用程序的静态资源在 wwwroot 文件夹中。

17 Windows

要在 Windows 下运行该应用程序,你需要使用 Visual Studio 构建和运行。

选择 MauiBlazorApp.WinUI3(Package) 项目作为你的启动项目:

file

同时为目标平台选择 x64:

file

然后你可以按 F5 或 Ctrl+F5,使 WinUI 应用作为本地 Windows 桌面应用运行。

file

18 Android

要在 Android 上运行该应用程序,首先使用 Android SDK 或 Android 设备管理器启动 Android 模拟器。

然后使用以下命令从 CLI 运行该应用程序:

dotnet build MauiBlazorApp -t:Run -f net6.0-android

要从 Visual Studio 在 Android 上运行,选择 MauiBlazorApp 项目作为启动项目:

file

然后在运行按钮下拉菜单中选择 net6.0-android 作为目标框架:

file

然后你可以点击 F5 或 Ctrl+F5,使用安卓模拟器运行该应用程序:

file

19 iOS 和 Mac Catalyst

译注:Mac Catalyst 是一个帮助开发者将 iOS 应用移植到 macOS 上的服务。

要运行 iOS 或 Mac Catalyst 的应用程序,你需要使用一个运行 Big Sur 的 macOS 开发环境。你目前不能从 Windows 开发环境中运行 iOS 或 Mac Catalyst 的应用程序,尽管我们确实期望 .NET MAUI 将支持使用连接的 Mac 构建代理或使用热重启在连接的设备上运行 iOS 应用程序。

要运行 iOS 和 Mac Catalyst 的应用程序,请使用以下命令:

dotnet build MauiBlazorApp -t:Run -f net6.0-ios
dotnet build MauiBlazorApp -t:Run -f net6.0-maccatalyst

file

在这个版本中,.NET MAUI Blazor 应用程序有一些已知的限制:

组件范围内的 CSS 文件(.razor.css)还不能在主 .NET MAUI 项目中使用。这将在未来的更新中得到修复。

了解更多关于 .NET 6 Preview 4 中 .NET MAUI 的新内容[8]。

20 其他性能改进

略...(注:这部分列举了一些社区贡献的提高性能的 PR,没啥可翻译的,感兴趣的同学可以直接在英文原文点击链接查看。)

21 提供反馈

我们希望你喜欢此次 .NET 6 中的 ASP.NET Core 预览版。我们很想听听你对这个版本的体验。在 GitHub[9] 上提交 Issue,让我们知道你的想法。

感谢你尝试使用 ASP.NET Core!

文中链接:

[1]. https://dotnet.microsoft.com/download/dotnet/6.0
[2]. https://docs.microsoft.com/dotnet/core/compatibility/6.0#aspnet-core
[3]. https://www.techempower.com/benchmarks/
[4]. https://github.com/aspnet/Announcements/issues/463
[5]. https://docs.microsoft.com/aspnet/core/fundamentals/http-logging
[6]. https://aka.ms/build2021-hotreload
[7]. https://aka.ms/picture-fixer
[8]. https://devblogs.microsoft.com/dotnet/announcing-net-maui-preview-4
[9]. https://github.com/dotnet/aspnetcore/issues
07
6

重读王小波

0
归档:2021年6月分类:点滴生活

这几日开始重读王小波的作品,打算从《沉默的大多数》到《黄金时代》、《白银时代》、《青铜时代》和《革命时期的爱情》,然后把王小波全集剩下的文章都读一遍。前面这四本书是我在大学的时候读过的图书,那时候大一宿舍有一位江苏的舍友,他姐姐是南京师范大学的中文系学生,极力推荐他读王小波,我受他的影响读完了这几本书。那时候读王小波,觉得很奇特,但我觉得自己并没有完全读懂他。虽然如此,我相信王小波对我的影响很大,这种影响是不知不觉中的影响,那时候中文互联网还很自由,我们都还可以使用谷歌的手气不错,那时候真的是中国互联网的黄金时代。

这次我放弃那种囫囵吞枣、一目十行的阅读方法,而是细致阅读,特别是阅读《沉默的大多数》的时候。这两天读到《中国知识分子与中古遗风》里面关于知识分子最害怕的事情,王小波真是太睿智了,这段话至今依然令人惊叹:

“我也有一个问题,是这样的:什么是知识分子最害怕的事?而且我也有答案,自以为经得起全球知识分子的质疑,那就是:知识分子最怕活在不理智的年代。所谓不理智的年代,就是伽利略低头认罪,承认地球不转的年代,也是拉瓦锡上断头台的年代;是茨威格服毒自杀的年代,也是老舍跳进太平湖的年代。

“我认为,知识分子的长处只是会以理服人,假如不讲理,他就没有长处,只有短处,活着没意思,不如死掉。丹麦王子哈姆雷特说:活着呢,还是死去,这是问题。但知识分子赶上这么个年代,死活不是问题。最大的问题是:这个倒霉的年头儿何时过去。假如能赶上这年头过去,就活着;赶不上了就犯不着再拖下去。

“老舍先生自杀的年代,我已经懂事了,认识不少知识分子。虽然我当时是个孩子,但嘴很严,所以也是他们谈话的对象。就我所知,他们最关心的正是赶得上赶不上的问题。在那年头死掉的知识分子,只要不是被杀,准是觉得赶不上好年头了。而活下来的准觉得自己还能赶上——当然,被改造好了、不再是知识分子的人不在此列。因此我对自己的答案颇有信心,敢拿这事和天下人打赌,知识分子最大的不幸,就是这种不理智。”

07
6

隐形马尔可夫模型(HMM)

0
归档:2021年6月分类:数海泛舟

什么是熵(Entropy)

简单来说,熵是表示物质系统状态的一种度量,用它老表征系统的无序程度。熵越大,系统越无序,意味着系统结构和运动的不确定和无规则;反之,,熵越小,系统越有序,意味着具有确定和有规则的运动状态。熵的中文意思是热量被温度除的商。负熵是物质系统有序化,组织化,复杂化状态的一种度量。

熵最早来原于物理学. 德国物理学家鲁道夫·克劳修斯首次提出熵的概念,用来表示任何一种能量在空间中分布的均匀程度,能量分布得越均匀,熵就越大。

一滴墨水滴在清水中,部成了一杯淡蓝色溶液热水晾在空气中,热量会传到空气中,最后使得温度一致更多的一些生活中的例子:

熵力的一个例子是耳机线,我们将耳机线整理好放进口袋,下次再拿出来已经乱了。让耳机线乱掉的看不见的“力”就是熵力,耳机线喜欢变成更混乱。熵力另一个具体的例子是弹性力。一根弹簧的力,就是熵力。 胡克定律其实也是一种熵力的表现。万有引力也是熵力的一种(热烈讨论的话题)。浑水澄清

file

于是从微观看,熵就表现了这个系统所处状态的不确定性程度。香农,描述一个信息系统的时候就借用了熵的概念,这里熵表示的是这个信息系统的平均信息量(平均不确定程度)。

最大熵模型

我们在投资时常常讲不要把所有的鸡蛋放在一个篮子里,这样可以降低风险。在信息处理中,这个原理同样适用。在数学上,这个原理称为最大熵原理(the maximum entropy principle)。

让我们看一个拼音转汉字的简单的例子。假如输入的拼音是"wang-xiao-bo",利用语言模型,根据有限的上下文(比如前两个词),我们能给出两个最常见的名字“王小波”和“王晓波 ”。至于要唯一确定是哪个名字就难了,即使利用较长的上下文也做不到。当然,我们知道如果通篇文章是介绍文学的,作家王小波的可能性就较大;而在讨论两岸关系时,台湾学者王晓波的可能性会较大。在上面的例子中,我们只需要综合两类不同的信息,即主题信息和上下文信息。虽然有不少凑合的办法,比如:分成成千上万种的不同的主题单独处理,或者对每种信息的作用加权平均等等,但都不能准确而圆满地解决问题,这样好比以前我们谈到的行星运动模型中的小圆套大圆打补丁的方法。在很多应用中,我们需要综合几十甚至上百种不同的信息,这种小圆套大圆的方法显然行不通。

数学上最漂亮的办法是最大熵(maximum entropy)模型,它相当于行星运动的椭圆模型。“最大熵”这个名词听起来很深奥,但是它的原理很简单,我们每天都在用。说白了,就是要保留全部的不确定性,将风险降到最小。

回到我们刚才谈到的拼音转汉字的例子,我们已知两种信息,第一,根据语言模型,wangxiao-bo可以被转换成王晓波和王小波;第二,根据主题,王小波是作家,《黄金时代》的作者等等,而王晓波是台湾研究两岸关系的学者。因此,我们就可以建立一个最大熵模型,同时满足这两种信息。现在的问题是,这样一个模型是否存在。匈牙利著名数学家、信息论最高奖香农奖得主希萨(Csiszar)证明,对任何一组不自相矛盾的信息,这个最大熵模型不仅存在,而且是唯一的。而且它们都有同一个非常简单的形式 -- 指数函数。下面公式是根据上下文(前两个词)和主题预测下一个词的最大熵模型,其中 w3 是要预测的词(王晓波或者王小波)w1 和 w2 是它的前两个字(比如说它们分别是“出版”,和“”),也就是其上下文的一个大致估计,subject 表示主题。

file

我们看到,在上面的公式中,有几个参数lambda和Z,他们需要通过观测数据训练出来。最大熵模型在形式上是最漂亮的统计模型,而在实现上是最复杂的模型之一。

我们上次谈到用最大熵模型可以将各种信息综合在一起。我们留下一个问题没有回答,就是如何构造最大熵模型。我们已经所有的最大熵模型都是指数函数的形式,现在只需要确定指数函数的参数就可以了,这个过程称为模型的训练。

最原始的最大熵模型的训练方法是一种称为通用迭代算法 GIS(generalized iterative scaling) 的迭代 算法。GIS 的原理并不复杂,大致可以概括为以下几个步骤:

  1. 假定第零次迭代的初始模型为等概率的均匀分布。
  2. 用第 N 次迭代的模型来估算每种信息特征在训练数据中的分布,如果超过了实际的,就把相应的模型参数变小;否则,将它们便大。
  3. 重复步骤 2 直到收敛。

GIS 最早是由 Darroch 和 Ratcliff 在七十年代提出的。但是,这两人没有能对这种算法的物理含义进行很好地解释。后来是由数学家希萨(Csiszar)解释清楚的,因此,人们在谈到这个算法时,总是同时引用 Darroch 和Ratcliff 以及希萨的两篇论文。GIS 算法每次迭代的时间都很长,需要迭代很多次才能收敛,而且不太稳定,即使在 64 位计算机上都会出现溢出。因此,在实际应用中很少有人真正使用 GIS。大家只是通过它来了解最大熵模型的算法。

八十年代,很有天才的孪生兄弟的达拉皮垂(Della Pietra)在 IBM 对 GIS 算法进行了两方面的改进,提出了改进迭代算法 IIS(improved iterative scaling)。这使得最大熵模型的训练时间缩短了一到两个数量级。这样最大熵模型才有可能变得实用。即使如此,在当时也只有 IBM 有条件是用最大熵模型。

由于最大熵模型在数学上十分完美,对科学家们有很大的诱惑力,因此不少研究者试图把自己的问题用一个类似最大熵的近似模型去套。谁知这一近似,最大熵模型就变得不完美了,结果可想而知,比打补丁的凑合的方法也好不了多少。于是,不少热心人又放弃了这种方法。第一个在实际信息处理应用中验证了最大熵模型的优势的,是宾夕法尼亚大学马库斯的另一个高徒原 IBM 现微软的研究员拉纳帕提(Adwait Ratnaparkhi)。拉纳帕提的聪明之处在于他没有对最大熵模型进行近似,而是找到了几个最适合用最大熵模型、而计算量相对不太大的自然语言处理问题,比如词性标注和句法分析。拉纳帕提成功地将上下文信息、词性(名词、动词和形容词等)、句子成分(主谓宾)通过最大熵模型结合起来,做出了当时世界上最好的词性标识系统和句法分析器。拉纳帕提的论文发表后让人们耳目一新。拉纳帕提的词性标注系统,至今仍然是使用单一方法最好的系统。科学家们从拉纳帕提的成就中,又看到了用最大熵模型解决复杂的文字信息处理的希望。

但是,最大熵模型的计算量仍然是个拦路虎。我在学校时花了很长时间考虑如何简化最大熵模型的计算量。终于有一天,我对我的导师说,我发现一种数学变换,可以将大部分最大熵模型的训练时间在 IIS 的基础上减少两个数量级。我在黑板上推导了一个多小时,他没有找出我的推导中的任何破绽,接着他又回去想了两天,然后告诉我我的算法是对的。从此,我们就建造了一些很大的最大熵模型。这些模型比修修补补的凑合的方法好不少。即使在我找到了快速训练算法以后,为了训练一个包含上下文信息,主题信息和语法信息的文法模型(language model),我并行使用了20 台当时最快的 SUN 工作站,仍然计算了三个月。由此可见最大熵模型的复杂的一面。

最大熵模型,可以说是集简与繁于一体,形式简单,实现复杂。值得一提的是,在Google的很多产品中,比如机器翻译,都直接或间接地用到了最大熵模型。

讲到这里,读者也许会问,当年最早改进最大熵模型算法的达拉皮垂兄弟这些年难道没有做任何事吗?他们在九十年代初贾里尼克离开 IBM 后,也退出了学术界,而到在金融界大显身手。他们两人和很多 IBM 语音识别的同事一同到了一家当时还不大,但现在是世界上最成功对冲基金(hedge fund)公司----文艺复兴技术公司 (Renaissance Technologies)。我们知道,决定股票涨落的因素可能有几十甚至上百种,而最大熵方法恰恰能找到一个同时满足成千上万种不同条件的模型。达拉皮垂兄弟等科学家在那里,用于最大熵模型和其他一些先进的数学工具对股票预测,获得了巨大的成功。从该基金 1988 年创立至今,它的净回报率高达平均每年 34%。也就是说,如果 1988 年你在该基金投入一块钱,今天你能得到 200 块钱。这个业绩,远远超过股神巴菲特的旗舰公司伯克夏哈撒韦(Berkshire Hathaway)。同期,伯克夏哈撒韦的总回报是 16 倍。

值得一提的是,信息处理的很多数学手段,包括隐含马尔可夫模型、子波变换、贝叶斯网络等等,在华尔街多有直接的应用。由此可见,数学模型的作用。

HMM(隐马尔可夫模型)

隐马尔可夫模型(Hidden Markov Model,HMM)是统计模型,它用来描述一个含有隐含未知参数的马尔可夫过程。其难点是从可观察的参数中确定该过程的隐含参数。然后利用这些参数来作进一步的分析,例如模式识别。

是在被建模的系统被认为是一个马尔可夫过程与未观测到的(隐藏的)的状态的统计马尔可夫模型。

下面用一个简单的例子来阐述:

假设我手里有三个不同的骰子。第一个骰子是我们平常见的骰子(称这个骰子为D6),6个面,每个面(1,2,3,4,5,6)出现的概率是1/6。第二个骰子是个四面体(称这个骰子为D4),每个面(1,2,3,4)出现的概率是1/4。第三个骰子有八个面(称这个骰子为D8),每个面(1,2,3,4,5,6,7,8)出现的概率是1/8。

file

假设我们开始掷骰子,我们先从三个骰子里挑一个,挑到每一个骰子的概率都是1/3。然后我们掷骰子,得到一个数字,1,2,3,4,5,6,7,8中的一个。不停的重复上述过程,我们会得到一串数字,每个数字都是1,2,3,4,5,6,7,8中的一个。例如我们可能得到这么一串数字(掷骰子10次):1 6 3 5 2 7 3 5 2 4

这串数字叫做可见状态链。但是在隐马尔可夫模型中,我们不仅仅有这么一串可见状态链,还有一串隐含状态链。在这个例子里,这串隐含状态链就是你用的骰子的序列。比如,隐含状态链有可能是:D6 D8 D8 D6 D4 D8 D6 D6 D4 D8

一般来说,HMM中说到的马尔可夫链其实是指隐含状态链,因为隐含状态(骰子)之间存在转换概率(transition probability)。在我们这个例子里,D6的下一个状态是D4,D6,D8的概率都是1/3。D4,D8的下一个状态是D4,D6,D8的转换概率也都一样是1/3。这样设定是为了最开始容易说清楚,但是我们其实是可以随意设定转换概率的。比如,我们可以这样定义,D6后面不能接D4,D6后面是D6的概率是0.9,是D8的概率是0.1。这样就是一个新的HMM。

同样的,尽管可见状态之间没有转换概率,但是隐含状态和可见状态之间有一个概率叫做输出概率(emission probability)。就我们的例子来说,六面骰(D6)产生1的输出概率是1/6。产生2,3,4,5,6的概率也都是1/6。我们同样可以对输出概率进行其他定义。比如,我有一个被赌场动过手脚的六面骰子,掷出来是1的概率更大,是1/2,掷出来是2,3,4,5,6的概率是1/10。

file

file

其实对于HMM来说,如果提前知道所有隐含状态之间的转换概率和所有隐含状态到所有可见状态之间的输出概率,做模拟是相当容易的。但是应用HMM模型时候呢,往往是缺失了一部分信息的,有时候你知道骰子有几种,每种骰子是什么,但是不知道掷出来的骰子序列;有时候你只是看到了很多次掷骰子的结果,剩下的什么都不知道。如果应用算法去估计这些缺失的信息,就成了一个很重要的问题。这些算法我会在下面详细讲。

如果你只想看一个简单易懂的例子,就不需要往下看了。

说两句废话,答主认为呢,要了解一个算法,要做到以下两点:会其意,知其形。答主回答的,其实主要是第一点。但是这一点呢,恰恰是最重要,而且很多书上不会讲的。正如你在追一个姑娘,姑娘对你说“你什么都没做错!”你要是只看姑娘的表达形式呢,认为自己什么都没做错,显然就理解错了。你要理会姑娘的意思,“你赶紧给我道歉!”这样当你看到对应的表达形式呢,赶紧认错,跪地求饶就对了。数学也是一样,你要是不理解意思,光看公式,往往一头雾水。不过呢,数学的表达顶多也就是晦涩了点,姑娘的表达呢,有的时候就完全和本意相反。所以答主一直认为理解姑娘比理解数学难多了。

回到正题,和HMM模型相关的算法主要分为三类,分别解决三种问题:

1)知道骰子有几种(隐含状态数量),每种骰子是什么(转换概率),根据掷骰子掷出的结果(可见状态链),我想知道每次掷出来的都是哪种骰子(隐含状态链)。这个问题呢,在语音识别领域呢,叫做解码问题。这个问题其实有两种解法,会给出两个不同的答案。每个答案都对,只不过这些答案的意义不一样。第一种解法求最大似然状态路径,说通俗点呢,就是我求一串骰子序列,这串骰子序列产生观测结果的概率最大。第二种解法呢,就不是求一组骰子序列了,而是求每次掷出的骰子分别是某种骰子的概率。比如说我看到结果后,我可以求得第一次掷骰子是D4的概率是0.5,D6的概率是0.3,D8的概率是0.2.第一种解法我会在下面说到,但是第二种解法我就不写在这里了,如果大家有兴趣,我们另开一个问题继续写吧。

2)还是知道骰子有几种(隐含状态数量),每种骰子是什么(转换概率),根据掷骰子掷出的结果(可见状态链),我想知道掷出这个结果的概率。看似这个问题意义不大,因为你掷出来的结果很多时候都对应了一个比较大的概率。问这个问题的目的呢,其实是检测观察到的结果和已知的模型是否吻合。如果很多次结果都对应了比较小的概率,那么就说明我们已知的模型很有可能是错的,有人偷偷把我们的骰子給换了。

3)知道骰子有几种(隐含状态数量),不知道每种骰子是什么(转换概率),观测到很多次掷骰子的结果(可见状态链),我想反推出每种骰子是什么(转换概率)。这个问题很重要,因为这是最常见的情况。很多时候我们只有可见结果,不知道HMM模型里的参数,我们需要从可见结果估计出这些参数,这是建模的一个必要步骤。

问题阐述完了,下面就开始说解法。(0号问题在上面没有提,只是作为解决上述问题的一个辅助)

0.一个简单问题

其实这个问题实用价值不高。由于对下面较难的问题有帮助,所以先在这里提一下。
知道骰子有几种,每种骰子是什么,每次掷的都是什么骰子,根据掷骰子掷出的结果,求产生这个结果的概率。

file

解法无非就是概率相乘:

file

1.看见不可见的,破解骰子序列

这里我说的是第一种解法,解最大似然路径问题。举例来说,我知道我有三个骰子,六面骰,四面骰,八面骰。我也知道我掷了十次的结果(1 6 3 5 2 7 3 5 2 4),我不知道每次用了那种骰子,我想知道最有可能的骰子序列。

其实最简单而暴力的方法就是穷举所有可能的骰子序列,然后依照第零个问题的解法把每个序列对应的概率算出来。然后我们从里面把对应最大概率的序列挑出来就行了。如果马尔可夫链不长,当然可行。如果长的话,穷举的数量太大,就很难完成了。

另外一种很有名的算法叫做Viterbi algorithm. 要理解这个算法,我们先看几个简单的列子。

首先,如果我们只掷一次骰子:

file

看到结果为1.对应的最大概率骰子序列就是D4,因为D4产生1的概率是1/4,高于1/6和1/8.

把这个情况拓展,我们掷两次骰子:

file

结果为1,6.这时问题变得复杂起来,我们要计算三个值,分别是第二个骰子是D6,D4,D8的最大概率。显然,要取到最大概率,第一个骰子必须为D4。这时,第二个骰子取到D6的最大概率是

file

同样的,我们可以计算第二个骰子是D4或D8时的最大概率。我们发现,第二个骰子取到D6的概率最大。而使这个概率最大时,第一个骰子为D4。所以最大概率骰子序列就是D4 D6。

继续拓展,我们掷三次骰子:

file

同样,我们计算第三个骰子分别是D6,D4,D8的最大概率。我们再次发现,要取到最大概率,第二个骰子必须为D6。这时,第三个骰子取到D4的最大概率是

file

同上,我们可以计算第三个骰子是D6或D8时的最大概率。我们发现,第三个骰子取到D4的概率最大。而使这个概率最大时,第二个骰子为D6,第一个骰子为D4。所以最大概率骰子序列就是D4 D6 D4。

写到这里,大家应该看出点规律了。既然掷骰子一二三次可以算,掷多少次都可以以此类推。我们发现,我们要求最大概率骰子序列时要做这么几件事情。首先,不管序列多长,要从序列长度为1算起,算序列长度为1时取到每个骰子的最大概率。然后,逐渐增加长度,每增加一次长度,重新算一遍在这个长度下最后一个位置取到每个骰子的最大概率。因为上一个长度下的取到每个骰子的最大概率都算过了,重新计算的话其实不难。当我们算到最后一位时,就知道最后一位是哪个骰子的概率最大了。然后,我们要把对应这个最大概率的序列从后往前推出来。

2.谁动了我的骰子?

比如说你怀疑自己的六面骰被赌场动过手脚了,有可能被换成另一种六面骰,这种六面骰掷出来是1的概率更大,是1/2,掷出来是2,3,4,5,6的概率是1/10。你怎么办么?答案很简单,算一算正常的三个骰子掷出一段序列的概率,再算一算不正常的六面骰和另外两个正常骰子掷出这段序列的概率。如果前者比后者小,你就要小心了。比如说掷骰子的结果是:

file

要算用正常的三个骰子掷出这个结果的概率,其实就是将所有可能情况的概率进行加和计算。同样,简单而暴力的方法就是把穷举所有的骰子序列,还是计算每个骰子序列对应的概率,但是这回,我们不挑最大值了,而是把所有算出来的概率相加,得到的总概率就是我们要求的结果。这个方法依然不能应用于太长的骰子序列(马尔可夫链)。

我们会应用一个和前一个问题类似的解法,只不过前一个问题关心的是概率最大值,这个问题关心的是概率之和。解决这个问题的算法叫做前向算法(forward algorithm)。

首先,如果我们只掷一次骰子:

file

看到结果为1.产生这个结果的总概率可以按照如下计算,总概率为0.18:

file

把这个情况拓展,我们掷两次骰子:

file

看到结果为1,6.产生这个结果的总概率可以按照如下计算,总概率为0.05:

file

继续拓展,我们掷三次骰子:

file

看到结果为1,6,3.产生这个结果的总概率可以按照如下计算,总概率为0.03:

file

同样的,我们一步一步的算,有多长算多长,再长的马尔可夫链总能算出来的。用同样的方法,也可以算出不正常的六面骰和另外两个正常骰子掷出这段序列的概率,然后我们比较一下这两个概率大小,就能知道你的骰子是不是被人换了。

Viterbi algorithm

HMM(隐马尔可夫模型)是用来描述隐含未知参数的统计模型,举一个经典的例子:一个东京的朋友每天根据天气{下雨,天晴}决定当天的活动{公园散步,购物,清理房间}中的一种,我每天只能在twitter上看到她发的推“啊,我前天公园散步、昨天购物、今天清理房间了!”,那么我可以根据她发的推特推断东京这三天的天气。在这个例子里,显状态是活动,隐状态是天气。

任何一个HMM都可以通过下列五元组来描述:

:param obs:观测序列
:param states:隐状态
:param start_p:初始概率(隐状态)
:param trans_p:转移概率(隐状态)
:param emit_p: 发射概率 (隐状态表现为显状态的概率)

file

states = ('Rainy', 'Sunny')

observations = ('walk', 'shop', 'clean')

start_probability = {'Rainy': 0.6, 'Sunny': 0.4}

transition_probability = {
    'Rainy' : {'Rainy': 0.7, 'Sunny': 0.3},
    'Sunny' : {'Rainy': 0.4, 'Sunny': 0.6},
    }

emission_probability = {
    'Rainy' : {'walk': 0.1, 'shop': 0.4, 'clean': 0.5},
    'Sunny' : {'walk': 0.6, 'shop': 0.3, 'clean': 0.1},
}

求解最可能的天气

求解最可能的隐状态序列是HMM的三个典型问题之一,通常用维特比算法解决。维特比算法就是求解HMM上的最短路径(-log(prob),也即是最大概率)的算法。

稍微用中文讲讲思路,很明显,第一天天晴还是下雨可以算出来:

定义V[时间][今天天气] = 概率,注意今天天气指的是,前几天的天气都确定下来了(概率最大)今天天气是X的概率,这里的概率就是一个累乘的概率了。

因为第一天我的朋友去散步了,所以第一天下雨的概率V[第一天][下雨] = 初始概率[下雨] * 发射概率[下雨][散步] = 0.6 * 0.1 = 0.06,同理可得V[第一天][天晴] = 0.24 。从直觉上来看,因为第一天朋友出门了,她一般喜欢在天晴的时候散步,所以第一天天晴的概率比较大,数字与直觉统一了。

从第二天开始,对于每种天气Y,都有前一天天气是X的概率 X转移到Y的概率 Y天气下朋友进行这天这种活动的概率。因为前一天天气X有两种可能,所以Y的概率有两个,选取其中较大一个作为V[第二天][天气Y]的概率,同时将今天的天气加入到结果序列中

比较V[最后一天][下雨]和[最后一天][天晴]的概率,找出较大的哪一个对应的序列,就是最终结果。

Viterbi被广泛应用到分词,词性标注等应用场景。

原文:https://www.cnblogs.com/skyme/p/4651331.html

03
6

[转]龙应台的《目送》

0
归档:2021年6月分类:点滴生活

华安上小学第一天,我和他手牵着手,穿过好几条街,到维多利亚小学。九月初,家家户户院子里的苹果和梨树都缀满了拳头大小的果子,枝丫因为负重而沉沉下垂,越出了树篱,钩到过路行人的头发。

很多很多的孩子,在操场上等候上课的第一声铃响。小小的手,圈在爸爸的、妈妈的手心里,怯怯的眼神,打量着周遭。他们是幼儿园的毕业生,但是他们还不知道一个定律:一件事情的毕业,永远是另一件事情的开启。

铃声一响,顿时人影错杂,奔往不同方向,但是在那么多穿梭纷乱的人群里,我无比清楚地看着自己孩子的背影──就好像在一百个婴儿同时哭声大作时,你仍旧能够准确听出自己那一个的位置。华安背着一个五颜六色的书包往前走,但是他不断地回头;好像穿越一条无边无际的时空长河,他的视线和我凝望的眼光隔空交会。

我看着他瘦小的背影消失在门里。

十六岁,他到美国做交换生一年。我送他到机场。告别时,照例拥抱,我的头只能贴到他的胸口,好像抱住了长颈鹿的脚。他很明显地在勉强忍受母亲的深情。

他在长长的行列里,等候护照检验;我就站在外面,用眼睛跟着他的背影一寸一寸往前挪。终于轮到他,在海关窗口停留片刻,然后拿回护照,闪入一扇门,倏忽不见。

我一直在等候,等候他消失前的回头一瞥。但是他没有,一次都没有。

现在他二十一岁,上的大学,正好是我教课的大学。但即使是同路,他也不愿搭我的车。即使同车,他戴上耳机──只有一个人能听的音乐,是一扇紧闭的门。有时他在对街等候公交车,我从高楼的窗口往下看:一个高高瘦瘦的青年,眼睛望向灰色的海;我只能想象,他的内在世界和我的一样波涛深邃,但是,我进不去。一会儿公交车来了,挡住了他的身影。车子开走,一条空荡荡的街,只立着一只邮筒。

我慢慢地、慢慢地了解到,所谓父女母子一场,只不过意味着,你和他的缘分就是今生今世不断地在目送他的背影渐行渐远。你站立在小路的这一端,看着他逐渐消失在小路转弯的地方,而且,他用背影默默告诉你:不必追。

我慢慢地、慢慢地意识到,我的落寞,仿佛和另一个背影有关。

博士学位读完之后,我回台湾教书。到大学报到第一天,父亲用他那辆运送饲料的廉价小货车长途送我。到了我才发觉,他没开到大学正门口,而是停在侧门的窄巷边。卸下行李之后,他爬回车内,准备回去,明明启动了引擎,却又摇下车窗,头伸出来说:“女儿,爸爸觉得很对不起你,这种车子实在不是送大学教授的车子。”

我看着他的小货车小心地倒车,然后“噗噗”驶出巷口,留下一团黑烟。直到车子转弯看不见了,我还站在那里,一口皮箱旁。

每个礼拜到医院去看他,是十几年后的时光了。推着他的轮椅散步,他的头低垂到胸口。有一次,发现排泄物淋满了他的裤腿,我蹲下来用自己的手帕帮他擦拭,裙子也沾上了粪便,但是我必须就这样赶回台北上班。护士接过他的轮椅,我拎起皮包,看着轮椅的背影,在自动玻璃门前稍停,然后没入门后。

我总是在暮色沉沉中奔向机场。

火葬场的炉门前,棺木是一只巨大而沉重的抽屉,缓缓往前滑行。没有想到可以站得那么近,距离炉门也不过五米。雨丝被风吹斜,飘进长廊内。我掠开雨湿了前额的头发,深深、深深地凝望,希望记得这最后一次的目送。

我慢慢地、慢慢地了解到,所谓父女母子一场,只不过意味着,你和他的缘分就是今生今世不断地在目送他的背影渐行渐远。你站立在小路的这一端,看着他逐渐消失在小路转弯的地方,而且,他用背影默默告诉你:不必追。

26
5

社区正式发布.NET 6 Preview4

0
归档:2021年5月分类:C#和.NET

时间过得真快,.NET 5我还没有来得及研究,眼看.NET 6的正式版本马上就要来了,我一直期待这个版本,因为这是LTS版本,我们团队会迁移到这个版本上来。

社区一直在不断推进.NET 6的可用性,Build 2021大会发布了Preview4,这个版本更新很大,可能与最终的版本相差不大,,正式发布RC版本是在8月份,正式发布RTM版本是在11月 ,到时后.NET正式完成.NET Framwork和.NET Core以及Mono的功能统一,还是非常值得期待的。

新产品功能:

System.Text.Json support for IAsyncEnumerable
System.Text.Json: Writable DOM Feature
Microsoft.Extensions.Logging compile-time source generator
System.Linq enhancements
Significantly improved FileStream performance on Windows
Enhanced Date, Time and Time Zone support
CodeGen
.NET Diagnostics: EventPipe for Mono and Improved EventPipe Performance
IL trimming
Single-file publishing
CLI install of .NET 6 SDK Optional Workloads
Built-in SDK version checking
CLI Templates (dotnet new)

另外,还有.NET MAUI或多平台应用UI :允许开发人员构建适用于Windows、MacOS、ios 和android的应用程序,共享单一代码库和模板。
ASP.NET Blazor混合应用程序:帮助开发人员构建基于Web的跨平台桌面体验,从而利用本机设备功能。
对ARM的加强支持:包括 Silicon 和ARM64支持。

24
5

马尔可夫链(Markov Chain)

0
归档:2021年5月分类:数海泛舟

马尔可夫链是一种非常重要的随机过程模型,在排队论、预测等方面有非常多的应用,当年我考数学系的时候就是冲着学校有一位马尔可夫领域的顶级数学家,不过后来自己越走越偏,也没有来得及进修这个算法。

随机过程

讲马尔可夫链不得不提到随机过程。顾名思义,它其实就是个过程,比如今天下雨,那么明天下不下雨呢?后天下不下雨呢?从今天下雨到明天不下雨再到后天下雨,这就是个过程。那么怎么预测N天后到底下不下雨呢?这其实是可以利用公式进行计算的,随机过程就是这样一个工具,把整个过程进行量化处理,用公式就可以推导出来N天后的天气状况,下雨的概率是多少,不下雨的概率是多少。

说白了,随机过程就是一些统计模型,利用这些统计模型可以对自然界的一些事物进行预测和处理,比如天气预报,比如股票,比如市场分析,比如人工智能。它的应用还真是多了去了。

马尔可夫链 (Markov Chain)

马尔可夫链 (Markov Chain)是随机过程中的一种过程,到底是哪一种过程呢?好像一两句话也说不清楚,还是先看个例子吧。

比如一个人,每天中午12点的标配,仨状态:吃,玩,睡。这就是传说中的状态分布。

你想知道他n天后中午12点的状态么?是在吃,还是在玩,还是在睡?这些状态发生的概率分别都是多少?

先看个假设,他每个状态的转移都是有概率的,比如今天玩,明天睡的概率是几,今天玩,明天也玩的概率是几几,看图更清楚一点。

file

这个矩阵就是转移概率矩阵P,并且它是保持不变的,就是说第一天到第二天的转移概率矩阵跟第二天到第三天的转移概率矩阵是一样的。(这个叫时齐,不细说了,有兴趣的同学自行百度)。

有了这个矩阵,再加上已知的第一天的状态分布,就可以计算出第N天的状态分布了。

file

S1 是4月1号中午12点的的状态分布矩阵 [0.6, 0.2, 0.2],里面的数字分别代表吃的概率,玩的概率,睡的概率。

那么

4月2号的状态分布矩阵 S2 = S1 * P (俩矩阵相乘)。

4月3号的状态分布矩阵 S3 = S2 * P (跟S1无关,只跟S2有关)。

4月4号的状态分布矩阵 S4 = S3 * P (跟S1,S2无关,只跟S3有关)。

...

4月n号的状态分布矩阵 Sn = Sn-1 * P (只跟它前面一个状态Sn-1有关)。

总结

马尔可夫链就是这样一个任性的过程,它将来的状态分布只取决于现在,跟过去无关!就把下面这幅图想象成是一个马尔可夫链吧。实际上就是一个随机变量随时间按照Markov性进行变化的过程。

file

23
5

自由多元文化主义

0
归档:2021年5月分类:政治哲学

去年一整年在阅读政治哲学方面的图书,前段时间读完金里卡先生的《当代政治哲学》,开始对自由多元文化注意产生浓厚的兴趣,“多元文化主义的自由主义”在上世纪末以来,一直是欧美倡导的价值观,特别是澳大利亚、新西兰和加拿大,这三个主要的移民国家几乎把自由多元文化注意当成本国的主流价值观。

这段时间先后阅读了十几篇论文,包括周濂老师的《如果多元文化主义是答案,它的问题是什么》、《澳大利亚多元文化主义政策的形成》、《多元文化主义的兴衰?关于多样性社会中接纳和包容的新争论》,这是我的文献综述结果:

file

我觉得自己下一步可以集中重读罗尔斯和金里卡,做更深入的思考,目前我的初步感觉是:自由多元主义是非常美好的理想,而且也有实际操作的可能,现实中的澳大利亚、加拿大和新西兰做得也非常不错。即使如此,我仍然相信在宪政制度下要真正平稳地运行自由多元主义,其实需要更高的社会成本和更高的公民素质。

更进一步,我甚至认为自由多元文化主义有可能最适合的运用是:已经能保障基本自由和人权、并且收入水平达到发达国家水平的移民国家。这里我要说明:如果一个国家已经建立起了自由多元文化主义,那么哪怕物质收入水平下降到发展中国家,也适用自由多元文化主义这一价值体系。对于加拿大、澳大利亚和新西兰这几个著名的移民国家,如果轻易放弃自由多元主义,那么将是莫大的损失和悲哀。

对于美国和欧洲来说,践行自由多元主义(这里指让其成为社会主流),可能会面临很多困难和阻碍,但是我觉得这不是放弃理想的理由。

20
5

贝叶斯算法-垃圾邮件过滤器

0
归档:2021年5月分类:数海泛舟

垃圾邮件是一种令人头痛的顽症,困扰着所有的互联网用户。

正确识别垃圾邮件的技术难度非常大。传统的垃圾邮件过滤方法,主要有"关键词法"和"校验码法"等。前者的过滤依据是特定的词语;后者则是计算邮件文本的校验码,再与已知的垃圾邮件进行对比。它们的识别效果都不理想,而且很容易规避。

2002年,Paul Graham提出使用"贝叶斯推断"过滤垃圾邮件。他说,这样做的效果,好得不可思议。1000封垃圾邮件可以过滤掉995封,且没有一个误判。

另外,这种过滤器还具有自我学习的功能,会根据新收到的邮件,不断调整。收到的垃圾邮件越多,它的准确率就越高。

贝叶斯过滤器是一种统计学过滤器,建立在已有的统计结果之上。所以,我们必须预先提供两组已经识别好的邮件,一组是正常邮件,另一组是垃圾邮件。

我们用这两组邮件,对过滤器进行"训练"。这两组邮件的规模越大,训练效果就越好。Paul Graham使用的邮件规模,是正常邮件和垃圾邮件各4000封。

"训练"过程很简单。首先,解析所有邮件,提取每一个词。然后,计算每个词语在正常邮件和垃圾邮件中的出现频率。比如,我们假定"sex"这个词,在4000封垃圾邮件中,有200封包含这个词,那么它的出现频率就是5%;而在4000封正常邮件中,只有2封包含这个词,那么出现频率就是0.05%。(【注释】如果某个词只出现在垃圾邮件中,Paul Graham就假定,它在正常邮件的出现频率是1%,反之亦然。这样做是为了避免概率为0。随着邮件数量的增加,计算结果会自动调整。)

有了这个初步的统计结果,过滤器就可以投入使用了。

现在,我们收到了一封新邮件。在未经统计分析之前,我们假定它是垃圾邮件的概率为50%。(【注释】有研究表明,用户收到的电子邮件中,80%是垃圾邮件。但是,这里仍然假定垃圾邮件的"先验概率"为50%。)

我们用S表示垃圾邮件(spam),H表示正常邮件(healthy)。因此,P(S)和P(H)的先验概率,都是50%。

file

然后,对这封邮件进行解析,发现其中包含了sex这个词,请问这封邮件属于垃圾邮件的概率有多高?

我们用W表示"sex"这个词,那么问题就变成了如何计算P(S|W)的值,即在某个词语(W)已经存在的条件下,垃圾邮件(S)的概率有多大。

根据条件概率公式,马上可以写出

file

公式中,P(W|S)和P(W|H)的含义是,这个词语在垃圾邮件和正常邮件中,分别出现的概率。这两个值可以从历史资料库中得到,对sex这个词来说,上文假定它们分别等于5%和0.05%。另外,P(S)和P(H)的值,前面说过都等于50%。所以,马上可以计算P(S|W)的值:

file

因此,这封新邮件是垃圾邮件的概率等于99%。这说明,sex这个词的推断能力很强,将50%的"先验概率"一下子提高到了99%的"后验概率"。

做完上面一步,请问我们能否得出结论,这封新邮件就是垃圾邮件?

回答是不能。因为一封邮件包含很多词语,一些词语(比如sex)说这是垃圾邮件,另一些说这不是。你怎么知道以哪个词为准?

Paul Graham的做法是,选出这封信中P(S|W)最高的15个词,计算它们的联合概率。(【注释】如果有的词是第一次出现,无法计算P(S|W),Paul Graham就假定这个值等于0.4。因为垃圾邮件用的往往都是某些固定的词语,所以如果你从来没见过某个词,它多半是一个正常的词。)

所谓联合概率,就是指在多个事件发生的情况下,另一个事件发生概率有多大。比如,已知W1和W2是两个不同的词语,它们都出现在某封电子邮件之中,那么这封邮件是垃圾邮件的概率,就是联合概率。

在已知W1和W2的情况下,无非就是两种结果:垃圾邮件(事件E1)或正常邮件(事件E2)。

其中,W1、W2和垃圾邮件的概率分别如下:

如果假定所有事件都是独立事件(【注释】严格地说,这个假定不成立,但是这里可以忽略),那么就可以计算P(E1)和P(E2):

file
file

又由于在W1和W2已经发生的情况下,垃圾邮件的概率等于下面的式子:

file

file

将P(S)等于0.5代入,得到

file

将P(S|W1)记为P1,P(S|W2)记为P2,公式就变成

file

这就是联合概率的计算公式。

将上面的公式扩展到15个词的情况,就得到了最终的概率计算公式:

file

一封邮件是不是垃圾邮件,就用这个式子进行计算。这时我们还需要一个用于比较的门槛值。Paul Graham的门槛值是0.9,概率大于0.9,表示15个词联合认定,这封邮件有90%以上的可能属于垃圾邮件;概率小于0.9,就表示是正常邮件。

有了这个公式以后,一封正常的信件即使出现sex这个词,也不会被认定为垃圾邮件了。

公告栏

欢迎大家来到我的博客,我是dodoro,希望我的博客能给你带来帮助。