首页 > 其他分享 >ml.net例子笔记3-Infer.net概率机器学习库

ml.net例子笔记3-Infer.net概率机器学习库

时间:2023-12-17 11:45:49浏览次数:37  
标签:ml 模型 用户 文档 Variable net NET Infer

Infer.net

Infer.NET is a .NET Foundation project. It's also a part of ML.NET machine learning framework.

https://dotnet.github.io/infer/
https://gitee.com/mirrors_dotnet/infer

Infer.NET 是一个在图形模型中运行贝叶斯推理的框架,它也可以用于概率编程。可以使用 Infer.NET 来解决许多不同类型的机器学习问题,包括分类、推荐或集群等标准问题与针对特定领域问题的定制解决方案。Infer.NET 目前已被广泛应用于各个领域,包括信息检索、生物信息学、流行病学、视觉以及许多其它领域。

微软认为Infer.NET的基于模型的方法是区别于其它机器学习框架的主要特点。
约旦·马尔可夫,微软研究所的开发部主任,解释道:
“这让你结合领域知识导入模型。该框架能从模型中直接创建一个定制的机器学习算法。这就意味着,没有必要列出你要解决的问题,然后写出需要的学习算法,Infer.NET实际上为你构造一个学习算法,只要你提供出模型即可”
这种方法主要的优点就是帮助更好的理解为什么人工智能做出这种决定。随着系统中使用人工智能技术越来越流行,它就会变得越来越重要。

https://dotnet.github.io/infer/userguide/how%20Infer.NET%20works.html

相关的包

https://www.nuget.org/packages?q=Microsoft.ML.Probabilistic 相关的包目前是4个
There currently are four maintained Infer.NET nuget packages:

  1. **Microsoft.ML.Probabilistic **contains classes and methods needed to execute the inference code.
  2. Microsoft.ML.Probabilistic.Compiler contains the Infer.NET Compiler, which takes model descriptions written using the Infer.NET API and converts them into inference code. It also contains utilities for the visualization of the generated code.
  3. Microsoft.ML.Probabilistic.Learners contains complete machine learning applications including a classifier and a recommender system.
  4. Microsoft.ML.Probabilistic.Visualizers.Windows contains an alternative .NET Framework and Windows specific set of visualization tools for exploring and analyzing models.

例子

玩家概率预测

https://learn.microsoft.com/zh-cn/dotnet/machine-learning/how-to-guides/matchup-app-infer-net

设计模型
游戏 胜者 败者
1 玩家 0 玩家 1
2 玩家 0 玩家 3
3 玩家 0 玩家 4
4 玩家 1 玩家 2
5 玩家 3 玩家 1
6 玩家 4 玩家 2

仔细观察示例数据会发现玩家 3 和玩家 4 分别输赢过一次。 我们来看下使用概率性编程时的排名。

编写代码

vs中加入包

具体工程的地址参考:
https://gitee.com/iamops/x-unix-dotnet/tree/main/ml.net/infer.net/playerInfer

官方例子

例子的说明:
https://dotnet.github.io/infer/userguide/Infer.NET%20tutorials%20and%20examples.html
例子的代码:
https://gitee.com/mirrors_dotnet/infer/tree/main/src/Examples
Examples contains C# projects that illustrate how to use Infer.NET to solve a variety of different problems.

  • ClickThroughModel - a web search example of converting a sequence of clicks by the user into inferences about the relevance of documents.
  • ClinicalTrial - the clinical trial tutorial example with an interactive user interface.
  • InferNET101 - samples from Infer.NET 101 introduction to the basics of Microsoft Infer.NET programming.
  • ImageClassifier - an image search example of classifying tagged images.
  • LDA - this example provides Infer.NET implementations of the popular LDA model for topic modeling. The implementations pay special attention to scalability with respect to vocabulary size, and with respect to the number of documents. As such, they provide good examples for how to scale Infer.NET models in general.
  • MontyHall - an Infer.NET implementation of the Monty Hall problem, along with a graphical user interface.
  • MotifFinder - an Infer.NET implementation of a simple model for finding motifs in nucleotide sequences, which constitutes an important problem in bioinformatics.
Tutorials

使用DebugFull编译Tutorials

infer-main\src\Tutorials\bin\DebugFull\net472\Tutorials.exe

这个可以看到概率开发的例子

Clinical Trial

https://dotnet.github.io/infer/userguide/Clinical%20trial%20tutorial.html
 Infer.NET 中进行贝叶斯模型选择,以确定一种新的医学治疗方法是否有效。我们将构建两个模型,对应于有效或无效的治疗,并使用模型选择来确定每个模型的后验概率,给定一些虚构的临床试验数据。

健康挑战

本教程中的数据包括参加虚构临床试验的个人的结果。每个人都接受了新的治疗或安慰剂(给予安慰剂的个体在对照组中)。一个好的结果用表示,一个坏的结果用表示。数据如下:
// Data from clinical trial
VariableArray controlGroup =
Variable.Observed(new bool[] { false, false, true, false, false });
VariableArray treatedGroup =
Variable.Observed(new bool[] { true, false, true, true, true });
Range i = controlGroup.Range;
Range j = treatedGroup.Range;
请注意,我们还设置了几个范围 i 和 j,它们分别针对对照组和治疗组中的人。我们稍后会用到它们。
为了确定治疗是否有效,我们将建立两个数据模型:一个假设治疗有效果,另一个没有效果。为了进行贝叶斯模型选择,我们需要引入一个布尔随机变量,它在两个模型之间切换。在此分析中,我们将为该变量提供统一的先验。在真正的临床试验中,这种先验应该是什么需要一些思考——新疗法的_先验_有效性是什么?
// Prior on being effective treatment
Variable isEffective = Variable.Bernoulli(0.5);

因果关系

首先,让我们考虑治疗是否对结果有影响。在这种情况下,对照组和治疗组的人获得良好结果的概率是不同的。因为我们不知道这两种概率,所以我们用Beta先验为它们定义随机变量,并在推理过程中学习它们。模型的代码如下面的代码片段所示。为了实现模型选择,我们将此建模代码放在 if 块_中,以便模型仅在 isEffective 为 true 时才适用。
另请参阅:[对变量进行分支以创建混合](https://dotnet.github.io/infer/userguide/Branching on variables to create mixture models.html)模型和[计算模型选择的模型证据](https://dotnet.github.io/infer/userguide/Computing model evidence for model selection.html)
。_
Variable probIfTreated, probIfControl;
using (Variable.If(isEffective)) { // Model if treatment is effective
probIfControl = Variable.Beta(1, 1);
controlGroup[i] = Variable.Bernoulli(probIfControl).ForEach(i);
probIfTreated = Variable.Beta(1, 1);
treatedGroup[j] = Variable.Bernoulli(probIfTreated).ForEach(j);
}
变量 probIfTreated 和 probIfControl 在 if 块外部声明,但在内部定义。这意味着可以在 using 语句之外引用变量,这将允许我们稍后推断它们的值。
请注意,我们没有具体说明治疗是否有_良好的_效果,只是说它有一些效果。我们将能够通过比较 probIfTreated 和 probIfControl 的后验分布来查看它是否具有良好的效果。

一点背景

现在让我们考虑另一种模型,其中治疗没有效果,_即_背景模型。在这种情况下,两组人获得良好结果的概率是相同的。同样,这个概率的值是未知的,所以我们将在其上放置一个 Beta 先验。这一次,我们用来创建周围的 if 块,以便模型将应用于 isEffective 为 false 的情况下。你可以把它看作是前一个 if 块的 else 子句。
using (Variable.IfNot(isEffective)) { // Model if treatment is not effective
Variable probAll = Variable.Beta(1, 1);
controlGroup[i] = Variable.Bernoulli(probAll).ForEach(i);
treatedGroup[j] = Variable.Bernoulli(probAll).ForEach(j);
}
Variable.IfNot变量 probAll 在 if 块中声明和定义,因为我们稍后不会使用它。

临床准确性

我们现在已经完全定义了模型,可以继续推断感兴趣的分布。
InferenceEngine engine = new InferenceEngine(); Console.WriteLine("Probability treatment has an effect = " + engine.Infer(isEffective));
Console.WriteLine("Probability of good outcome if given treatment = "
+ (float)engine.Infer(probIfTreated).GetMean()); Console.WriteLine("Probability of good outcome if control = "
+ (float)engine.Infer(probIfControl).GetMean());
当我们运行这段代码时,它会打印出来:
Probability treatment has an effect = Bernoulli(0.7549)
Probability of good outcome if given treatment = 0.7142857
Probability of good outcome if control = 0.2857143
因此,从这些数据中有一些证据表明治疗有效果,而且效果是积极的。

因子图

这是这个模型的因子图_应该_是什么样子的(如果你把它保存成[DGML格式](https://dotnet.github.io/infer/userguide/inference engine settings.html" \l "savefactorgraphtofolder)):

但是,如果勾选“示例浏览器”中的框以显示因子图【】,或等效设置,则会看到以下内容:engine.ShowFactorGraph = true

由于图形绘制工具的限制,代码中的“if”块不会显式绘制。
相反,变量 isEffective 通过条件边缘指向 controlGroup 和 treatedGroup
条件边缘选择 controlGroup 和 treatedGroup 的哪个父级处于活动状态。

ClinicalTrial GUI

设置DebugCore 编译为.net6.0的程序

infer-main\src\Examples\ClinicalTrial\bin\DebugCore\net6.0-windows\ClinicalTrial.exe

鼠标左键可以在红绿框上点选或拖动,可以看到对应的概率变化
【键盘输入D,可以看到人员倒了】

ImageClassifier

https://dotnet.github.io/infer/userguide/Image%20classifier%20example.html
此示例演示如何构建两类贝叶斯点机并将其应用于图像分类问题。([此处](https://dotnet.github.io/infer/userguide/Learners/Bayes Point Machine classifiers.html)介绍了贝叶斯点机分类器的可靠、通用实现)。贝叶斯点机是通用的,可用于许多不同的应用程序 - 该代码将在后面详细描述。
首先,我们讨论应用程序。当您运行应用程序时,您首先看到的是一个包含大约 3 打图像的窗口,沿着窗口的顶部,有一个绿色区域和一个红色区域来拖动图像。
用户将一个或多个图像拖动到好或坏区域 - 每次发生这种情况时,您都会为特定图像创建一个好(绿色)或坏(红色)标签。
这些标记的图像用作示例,应用程序从这些示例中学习,并提供对所有未标记图像的好坏的评估。
未标记的图像向左或向右显示多远是分类器认为其好坏程度的度量。
public void Train(Vector[] data, bool[] labels)
{
nTrain.ObservedValue = data.Length;
trainingItems.ObservedValue = data;
trainingLabels.ObservedValue = labels;
if (!singleModel)
weightPosterior.ObservedValue = engine.Infer(weights);
}
public double[] Test(Vector[] data)
{
nTest.ObservedValue = data.Length;
testItems.ObservedValue = data;
Bernoulli[] labelPosteriors = engine.Infer<Bernoulli[]>(testLabels);
double[] probs = new double[data.Length];
for (int i = 0; i < probs.Length; i++)
{
probs[i] = labelPosteriors[i].GetProbTrue();
}
return probs;
}
这个有点神奇,把图片分别拖到上面的2个区域,下面的会自动归类

ClickThroughModel

https://dotnet.github.io/infer/userguide/Click%20through%20model%20sample.html
该代码演示了如何解决 Web 搜索中的问题:从用户在结果页面上的单击序列推断文档的相关性。

问题

在这里,我们考虑仅使用搜索引擎点击日志来联合推断文档片段的相关性和质量的问题。单击日志记录用户行为,以响应每个查询的检索到的文档排名列表。
对于每个用户会话,这对应于用户单击的文档的有序列表。
因此,日志提供了有关摘要质量和文档相关性的隐式反馈。
在这项工作中,我们的目标是使用点击数据来检测文档摘要的质量并推断文档的相关性。

方法

我们提出了一个图形模型,用于描述典型查询会话中的潜在用户行为。我们的概率模型直接对用户点击进行建模,从而解开摘要和相关性;请注意,摘要是特定于文档的,而文档与查询的相关性取决于其他文档。通过考虑一个文档与其他文档在排名中存在的影响,我们可以获得将摘要与相关性分开的信息。
给定一个同质用户群体的数据集(给定的查询对于特定信息需求是无歧义的)以及他们对文档整个排名的点击行为,我们推断用户对文档的特定检查行为,以及摘要的全局质量和文档的相关性,方法是在该用户模型中提出推理问题。

用户模型


上图显示了搜索会话期间的典型用户行为示例。
用户按顺序扫描搜索引擎检索到的排名结果,直到他们的信息需求得到满足或失去耐心。顺序扫描的一个重要含义是,如果用户尚未检查当前文档(未阅读摘要),则他们将不会检查任何将来的文档。用户按顺序检查检索到的结果,如果文档的摘要对他们有吸引力,则单击文档。
点击后,他们可以阅读文档的内容并决定文档是否相关。如果文件是相关的,他们仍然可以检查更多的文件(但概率较小);
如果文件不相关,他们可能会以更大的概率检查更多文件。无论哪种情况,用户都可以选择退出会话。

概率模型


上图显示了概率模型的一个切片,对应于表示用户行为的状态转换图。此图使用因子图表示法。感兴趣的主要变量显示为大圆圈。次要变量显示为较小的圆圈,因子显示为正方形,并用其函数进行标记。模型中以_单击_和 isRelevant 随机变量为条件的部分显示在虚线矩形内(这是“门”表示法)。对于每个用户,唯一的观察结果是他们的一组点击。鉴于此,我们进行概率推理来推断潜在变量的边际后验分布。
我们的目标是描述如何使用 Infer.NET 来指定与每个变量相关的条件概率,并对模型进行推理。描述用户是否继续检查下一个文档(显示在虚线矩形中)的逻辑取决于用户是否单击了当前文档,如果他们单击了,则该文档是否相关 - 为了简化此示例,假定与这三个条件对应的参数由用户指定, 因此,不要在图中显示为变量。
与此示例关联的解决方案文件具有图形用户界面,允许使用模型和创建合成数据。在下一节中,我们将介绍如何使用 GUI:

运行模型

下图显示了图形用户界面。与界面的交互发生在两个面板中:

标题为“用户模型:状态转换图”的面板:此面板中的轨迹栏用于指定模型参数,这些参数反映了根据先前操作执行特定操作的概率。例如,右上角的第一个跟踪栏用于指定P(examiner=true|examiner-1=true, clickr-1=false)
标题为“2 个文档的点击次数”的面板:在此面板中,用户可以模拟与用户行为相对应的数据。每个跟踪栏代表用户可以单击这两个文档的 4 种可能方式之一。
跟踪栏的值指定具有特定单击行为的用户数。为清楚起见,该接口设计为通过搜索查询检索 2 个文档,但底层实现对任意数量的文档都有效;尽管我们需要修改代码中的静态变量 nRanks。请注意,我们使用线程,以便模型在任何跟踪栏上的值发生更改时自动执行推理。

有趣的实验运行

尝试将左侧列中的用户计数设置为 0,并将其他用户计数保持为初始值 50。这意味着没有用户单击第一个文档,然后决定也查看第二个文档。这是第一份文件具有相关性的有力证据。然而,许多用户绕过第一个文档并立即点击第二个文档(如第 3 列所示)这一事实证明第一个文档的片段很差——这些结论是由推论得出的。
现在玩第 3 列,看看会发生什么,例如,当您将其降低到 0 或增加到 100 时。您应该会看到第二份文档的摘要申诉相应地有所不同。

过程
  • 初始化变量

在此示例中,我们假设表示用户是否会检查下一个文档的概率(取决于用户是否单击以及文档是否相关)由用户指定。这些值不需要在模型规范期间指定,只有在推理期间才需要。因此,我们只是将它们创建为新变量,并在运行模型之前暂缓为它们提供值:
Variable[] probNextIfNotClick = Variable.New(); Variable[] probNextIfClickNotRel = Variable.New(); Variable[] probNextIfClickRel = Variable.New();
通过相同的参数,用户数量也推迟到运行时:
Variable nUsers = Variable.New();
对于每个文档,我们都有其(与用户_无关的)_相关性摘要上诉,这些_摘要上诉_被创建为变量上的 .NET 数组【nRanks=2,本例2个文档】:
Variable[] appeal = new Variable[nRanks]; Variable[] relevance = new Variable[nRanks];
每_份文件都具有统一的吸引力和_相关性
for (int d = 0; d < nRanks; d++) {
appeal[d] = Variable.Beta(1, 1);
relevance[d] = Variable.Beta(1, 1);
}
对于每个用户和每个文档,我们都有变量,这些变量表示用户是否检查了文档,他们是否点击了文档,以及是否与他们相关。对于每个文档,对于每个文档排名,我们使用 VariableArrays 跨用户建模:
VariableArray[] examine = new VariableArray[nRanks]; VariableArray[] click = new VariableArray[nRanks]; VariableArray[] isRel = new VariableArray[nRanks];
观察点击,我们将在运行时为点击数组指定这些观察结果;但是,在指定模型时,我们不需要有观测值。
在每个排名中,这些变量 examineclick 和 isRel 在所有用户中都遵循相同的条件分布。因此,我们使用范围变量 user 来指定同时在所有用户中的分布。范围变量可以指定为:
Range u = new Range(nUsers).Named("User");
examine[d] = Variable.Array(u);
click[d] = Variable.Array(u);
isRel[d] = Variable.Array(u);

  • 指定模型

在 Infer.NET 中,我们可以像从模型中采样一样指定模型。在本节中,我们将详细阐述如何定义点击的概率模型。
首先,让我们考虑_一下 examine_ 变量。我们假设所有用户都检查了第一个文档 (d==0),以便:
examine[0][u] = Variable.Bernoulli(1).ForEach(u);
其他级别的文件根据以下规定的条件分配进行审查:
using (Variable.ForEach(u)) {
var nextIfClick = Variable.New();
using (Variable.If(isRel[d-1][u]))
nextIfClick.SetTo(Variable.Bernoulli(probNextIfClickRel));
using (Variable.IfNot(isRel[d-1][u]))
nextIfClick.SetTo(Variable.Bernoulli(probNextIfClickNotRel));
var nextIfNotClick = Variable.Bernoulli(probNextIfNotClick);
var next = (((!click[d - 1][u]) & nextIfNotClick) | (click[d - 1][u] & nextIfClick));
examine[d][u] = examine[d - 1][u] & next;
}
examine[d][u] 的条件概率表是使用 [Variable.If 和 Variable.IfNot](https://dotnet.github.io/infer/userguide/Branching on variables to create mixture models.html) 构造以及因子图中所示的“[and”和“or”因子](https://dotnet.github.io/infer/userguide/Boolean and comparison operations.html)指定的。变量 nextIfClicknextIfNotClick 和 next 在第 [1 页](https://dotnet.github.io/infer/userguide/Click through model sample.html)的因子图中用小圆圈表示。请注意,整段模型代码都嵌入在 [ForEach 块](https://dotnet.github.io/infer/userguide/ForEach blocks.html)中。这可确保对 Variable.Bernoulli 的调用为每个用户创建一个单独的变量。
_点击_变量的条件分布 isRel 变量在每个等级和所有用户中的条件分布现在可以指定为:
using (Variable.ForEach(u)) {
click[d][u] = examine[d][u] & Variable.Bernoulli(appeal[d]);
isRel[d][u] = click[d][u] & Variable.Bernoulli(relevance[d]);
}

  • 推理引擎的实例化

我们创建一个以 EP 作为推理算法的推理引擎:
InferenceEngine ie = new InferenceEngine(new ExpectationPropagation());

  • 指定参数和观测变量:

在此代码中,所有必需的参数和观察结果都由“用户”对象提供。使用它,我们按如下方式设置观测值:
nUsers.ObservedValue = user.nUsers;
probNextIfNotClick.ObservedValue = user.probExamine[0]; probNextIfClickNotRel.ObservedValue = user.probExamine[1]; probNextIfClickRel.ObservedValue = user.probExamine[2];
for (int d = 0; d < nRanks; d++)
click[d].ObservedValue = user.clicks[d];

  • 执行推理:

for (int d = 0; d < nRanks; d++) {
docStats[d].inferredRelevance = ie.Infer(relevance[d]);
docStats[d].inferredAppeal = ie.Infer(appeal[d]);
}

小结

  • Infer.net这个库现在统一到了Microsoft.ML.Probabilistic空间下,采用的是MIT许可证,以便在商业应用中免费使用
  • 通过模型定义,进行概率编程的模式
  • 采用基于模型的方法进行机器学习,开发人员为框架提供模型,然后框架直接从提供的模型中生成机器学习算法。许多学习模型要求程序员将他们的模型映射到预先存在的学习算法,然而,Infer.NET不是必须将您的问题映射到您已经获得的预先存在的学习算法上,而是根据您提供的模型为您构建学习算法。
  • 基于模型的机器学习的另一个优点是可解释性。如果您自己设计了模型并且学习算法遵循该模型,那么您可以理解系统为何以特定方式运行或进行某些预测。随着机器学习应用逐渐进入我们的生活,理解和解释他们的行为变得越来越重要。
  • 确定性推理算法的使用是对大多数其他概率编程框架的主要基于采样的方法的补充。Infer.net的一个关键功能是支持在线贝叶斯推理- 系统在新数据到来时学习的能力。这对于与用户实时交互的商业和消费产品至关重要
  • 当然,从如上的例子中也可以看到,这个的开发还是需要概率的一些背景知识才行,不然会不明白上面的算法

标签:ml,模型,用户,文档,Variable,net,NET,Infer
From: https://www.cnblogs.com/2018/p/17908846.html

相关文章

  • Kubernetes 调度场景实战指南
    Kubernetes调度是确保集群中的Pod在合适的节点上运行的关键组件。通过灵活配置调度策略,可以提高资源利用率、负载均衡和高可用性。在本文中,我们将深入研究一些实际的Kubernetes调度场景,并提供相应的配置示例和最佳实践。1.基础场景-NodeSelector场景描述:我们有一些节点标......
  • [.NET开发者的福音]一个方便易用的在线.NET代码编辑工具.NET Fiddle
    前言今天给大家分享一个方便易用的.NET在线代码编辑工具,能够帮助.NET开发人员快速完成代码编写、测试和分享的需求(.NET开发者的福音):.NETFiddle。.NETFiddle介绍我们可以不用再担心环境与庞大的IDE安装的问题,不管在任何时间,任何环境都可以在线运行调试!.NETFiddle是一个在......
  • 如何写出漂亮代码 https://libin9ioak.blog.csdn.net/article/details/127749042
    从代码的编写规范,格式的优化,设计原则和一些常见的代码优化的技巧等方面总结了45个小技巧: 1、规范命名命名是写代码中最频繁的操作,比如类、属性、方法、参数等。好的名字应当能遵循以下几点:见名知意比如需要定义一个变量需要来计数inti=0;1名称i没有任何的实际意义,没有......
  • CClinkIE转DEVICENET网关的应用主要体现在工业自动化领域
    CClinkIE转DEVICENET网关的应用主要体现在工业自动化领域。在工业生产过程中,设备之间的通信和数据传输是非常重要的。CClinkIE转DEVICENET网关作为连接两种不同通信协议的桥梁,可以实现设备之间的无缝通信,提高生产效率。CClinkIE转DEVICENET网关还具有易于扩展和升级的特点。随着工......
  • ml.net例子笔记2-概念和Widnows AI Studio
    一机器学习和ml.net1Python机器学习库在Python中,工具和库的生态系统可以分为五个主要领域:数据处理数据可视化数值计算模型训练神经网络这可能不全,因为此外还有其他许多的库,它们负责其他任务,并专注于机器学习的一些特定领域,比如自然语言处理和图像识别。使用Python......
  • Kubernetes: client-go 源码剖析(二)
    上接Kubernetes:client-go源码剖析(一)2.3运行informer运行informer将Reflector,informer和indexer组件关联以实现informer流程图的流程。2.3.1ReflectorList&Watch运行informer:informer.Run(stopCh)//client-go/tools/cache/shared_informer.gofunc(s*s......
  • Kubernetes: client-go 源码剖析(一)
    0.前言在看kube-scheduler组件的过程中遇到了kube-scheduler对于client-go的调用,泛泛的理解调用过程总有种隔靴搔痒的感觉,于是调转头先把client-go理清楚在回来看kube-scheduler。为什么要看client-go,并且要深入到原理,源码层面去看。很简单,因为它很重要。重要在两方......
  • 将自己的数据转为Randla-net支持的格式
     fromsklearn.neighborsimportKDTreefromos.pathimportjoin,exists,dirname,abspathimportnumpyasnpimportos,glob,pickleimportsysBASE_DIR=dirname(abspath(__file__))ROOT_DIR=dirname(BASE_DIR)sys.path.append(BASE_DIR)sys.path.append(......
  • 初中英语优秀范文100篇-028How to Be a Good Internet User-如何成为一名合格的网民
    PDF格式公众号回复关键字:SHCZFW028记忆树1Withthedevelopmentofthetechnology,mostofusareabletousetheInternet.翻译随着科技的发展,我们大多数人都能够使用互联网。简化记忆互联网句子结构这句话的结构是:时间状语从句(Withthedevelopmentofthet......
  • .NET微信网页开发之通过UnionID机制解决多应用用户帐号统一问题
    背景随着公司微信相关业务场景的不断拓展,从最初的一个微信移动应用、然后发展成微信公众号应用、然后又有了微信小程序应用。但是随着应用的拓展,如何保证相同用户的微信用户在不同应用中登录的同一个账号呢?今天的主题就来了.NET微信网页开发之通过UnionID机制解决多应用用户帐号......