将.NET AI模块添加到CodeProject.AI Server

目录

介绍

选择模块

将模块添加到CodeProject.AI服务器

创建模块的项目

创建modulesettings.json文件

modulesettings.development.json

从示例代码中复制代码和资产

包括其他NuGet和项目依赖项

重构示例代码以供我们使用

创建请求处理器类

连接一切

测试一下

附加奖励——在Windows上部署XCOPY模块

总结


介绍

本文将介绍如何通过调整现有.NET代码并添加适配器来为 CodeProject.AI Server 创建模块。这将使原始代码的功能能够由CodeProject.AI Server作为HTTP端点公开。该过程类似于将自己的Python模块添加到CodeProject.AI,只不过我们将使用.NET而不是Python

在开始之前,可能值得确保您已阅读向CodeProject.AI添加新模块

选择模块

您可以从头开始编写自己的模块,也可以使用当前可用的大量开源AI项目之一。有数千个,对于本文,我选择了 DotNet 示例存储库中的 TextClassificationTF 示例。此项目采用一些文本,并返回文本是否具有正面或负面情绪。

将模块添加到CodeProject.AI服务器

首先,请确保已从GitHub克隆(或下载)CodeProject.AI Server代码

要将我们选择的模块添加到CodeProject.AI,我们需要执行以下操作(稍后我们将详细介绍每个步骤):

  1. 在CodeProject.AI Server解决方案中创建一个新项目来保存我们的模块。
  2. 在项目中创建一个modulesettings.json文件,以配置将由CodeProject.AI Server为此模块公开的终结点。CodeProject.AI Server 将在启动时发现此文件,配置定义的端点,并启动模块的可执行文件。
  3. 将要添加到刚创建的项目中的模块中的代码复制过来。为了整洁起见,最好在项目中创建一个子文件夹,用于存放来自其他模块的整个代码。

目标是确保轻松包含更新。毫无疑问,您使用的代码会随着时间的推移而更新。很高兴能够获取更新的代码并将其放入同一个子文件夹中。即时升级。差不多有一点管道。

  1. 添加对原始代码引用的任何NuGet包的引用。
  2. 如果需要,请重构复制的代码以供常规使用。这通常是必需的,因为您可能会发现许多代码段可能具有硬连线的值或位置,可能被设计为作为API或命令行调用,或者位于Web应用程序控制器的某个深处。

您可能需要进行一些(希望是)小的更改,以公开适配器可以调用的函数,或者将硬连线值替换为通过环境变量提供的值。再说一遍:你需要做的改变越少越好,但有些可能是不可避免的。

  1. 通过派生一个类来处理从CodeProject.AI Server接收的请求并返回响应,从而创建CodeProject.AI Server适配器。您将从抽象基类派生,并且只需提供处理请求的方法。所有样板服务器/模块通信和错误处理都为您处理。
  2. 对项目的Program.cs文件进行细微更改,以将程序配置为运行上述代码。
  3. 测试。这可以通过使用Postman等工具或编写一个简单的网页来调用CodeProject.AI Server上的新终结点来完成。

创建模块的项目

NET 6中编写CodeProject.AI模块时,您正在创建一些内容,用于轮询CodeProject CodeProject.AI Server中为该模块创建的队列中的命令。最简单的方法是在src/AnalysisLayer下的文件夹中创建一个Worker Service项目。CodeProject.AI Server扫描此文件夹中的目录中的模块元数据,从而允许服务器启动模块。

执行此操作的步骤如下:

  • 右键单击“解决方案资源管理器”中的“src/AnalyisLayer”文件夹
    • 选择添加”-“>新项目
    • 选择适用于C#的辅助角色服务”项目模板
    • 单击下一步

这将打开项目配置对话框:

  • 项目名称”设置为“SentimentAnalysis
  • 位置”设置为CodeProject.AI解决方案副本中的src\AnalysisLayer目录。
  • 单击下一步”。

这将打开其他信息对话框:

我们不需要在此处进行任何更改,因此请单击创建。这将创建具有以下结构的项目:

创建modulesettings.json文件

modulesettings.json文件为

  • 是否应该启动
  • 如何开始
  • 它在什么平台上运行
  • CodeProject.AI Server将为此模块公开的端点。在这种情况下,我们将
    • 公开 http://localhost:5000/v1/text/sentiment
    • 使用HTTP POST方法
    • 发送一个表单变量text,该变量将包含要分析的文本
    • 并期望JSON有效负载响应
      • 一个Boolean success属性,该属性指示操作是否已成功完成
      • 一个Boolean is_positive属性,指示输入文本是否具有积极的情绪
      • 输入文本具有积极情绪的概率的浮点数positive_probability,其中0.5为中性。

在我们的例子中,modulesettings.json文件将如下所示:

{
  "Modules": {
    "SentimentAnalysis": {
      "Name": "Sentiment Analysis",
      "Activate": true,
      "Description": "Determines if a comment is positive or negative",
      "FilePath": "SentimentAnalysis\\SentimentAnalysis.dll",
      "Runtime": "dotnet",
      "Platforms": [ "windows", "linux", "docker" ],
      "RouteMaps": [
        {
          "Name": "Sentiment Analysis",
          "Path": "text/sentiment",
          "Method": "POST",
          "Queue": "sentimentanalysis_queue",
          "Command": "sentiment",
          "Description": "Determines if the supplied text has a positive 
                          or negative sentiment",
          "Inputs": [
            {
              "Name": "text",
              "Type": "Text",
              "Description": "The text to be analyzed."
            }
          ],
          "Outputs": [
            {
              "Name": "success",
              "Type": "Boolean",
              "Description": "True if successful."
            },
            {
              "Name": "is_positive",
              "Type": "Boolean",
              "Description": "Whether the input text had a positive sentiment."
            },
            {
              "Name": "positive_probability",
              "Type": "Float",
              "Description": "The probability the input text has a positive sentiment."
            }
          ]
        }
      ]
    }
  }
}

modulesettings.development.json

此文件将替代开发环境的某些modulesettings.json文件值。在这种情况下,可执行文件的位置将位于bin\debug\net6.0目录中,而不是模块的根文件夹中:

{
  "Modules": {
    "SentimentAnalysis": {
      "FilePath": "SentimentAnalysis\\bin\\debug\\net6.0\\SentimentAnalysis.dll"
    }
  }
}

从示例代码中复制代码和资产

情绪分析代码使用的数据和模型包含在示例存储库的sentiment_model文件夹中。将此文件夹复制到新模块项目。

使用数据和模块的代码都包含在示例代码的Program.cs文件中。复制此代码

  • 创建新的类文件TextClassifier.cs
  • 将此文件中TextClassifier类的内容替换为示例代码的 Program.cs 文件中Program类的内容。我们将在下一步中修复此问题。

包括其他NuGet和项目依赖项

为了构建此项目,必须包含一些依赖项:

  • 使用Microsoft的ML.NET框架及其对TensorFlow模型的支持所需的NuGet包。
    • Microsoft.ML
    • Microsoft.ML.SampleUtils
    • Microsoft.ML.TensorFlow
    • SciScharp.TensorFlow.Redist
  • 使用CodeProject.AI NET SDK的项目
    • CodeProject.AI.AnalsisLayer.SDK

重构示例代码以供我们使用

示例代码中的代码旨在作为一个特定示例,其中包含硬编码的输入和大量Console.WriteLine语句,以显示代码操作的大量详细信息。我们通过以下方式更新了代码

  • main方法转换为类构造函数TextClassifier
  • 将一些变量变成字段
  • 更改PredictSentiment方法以采用参数,而不是使用硬编码值。

由于更改的实际细节不是我们试图通过本文完成的内容,因此此处未显示代码。这些更改的结果可以在存储库中的代码中看到。

创建请求处理器类

倒数第二个编码步骤是创建后台工作线程,该工作线程将从CodeProject.AI Server检索请求、处理请求并返回结果。在更新的SDK中,其中大部分已封装在一个抽象类CommandQueueWorker中。我们所要做的就是创建一个新的类文件 SentimentAnalysisWorker.cs 并在此文件中

  • 创建一个响应类SentimentAnalysisResponse,从BackendSuccessResponse中派生定义模块响应的结构。
  • 重写SentimentAnalysisWorker.ProcessRequest方法和
  • 创建SentimentAnalysisWorker构造函数以初始化特定于模块的功能。

完整的SentimentAnalysisWorker.cs文件为:

using CodeProject.AI.AnalysisLayer.SDK;

namespace SentimentAnalysis
{
    class SentimentAnalysisResponse : BackendSuccessResponse
    {
        /// <summary>
        /// Gets or set a value indicating whether the text is positive.
        /// </summary>
        public bool? is_positive { get; set; }

        /// <summary>
        /// Gets or sets the probablity of being positive.
        /// </summary>
        public float? positive_probability { get; set; }
    }

    public class SentimentAnalysisWorker : CommandQueueWorker
    {
        private const string _defaultModuleId  = "sentiment-analysis";
        private const string _defaultQueueName = "sentimentanalysis_queue";
        private const string _moduleName       = "Sentiment Analysis";

        private readonly TextClassifier _textClassifier;

        /// <summary>
        /// Initializes a new instance of the SentimentAnalysisWorker.
        /// </summary>
        /// <param name="logger">The Logger.</param>
        /// <param name="textClassifier">The TextClassifier.</param>
        /// <param name="configuration">The app configuration values.</param>
        public SentimentAnalysisWorker(ILogger<SentimentAnalysisWorker> logger,
                                       TextClassifier textClassifier,  
                                       IConfiguration configuration)
            : base(logger, configuration, _moduleName, _defaultQueueName, _defaultModuleId)
        {
            _textClassifier  = textClassifier;
        }

        /// <summary>
        /// The work happens here.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>The response.</returns>
        public override BackendResponseBase ProcessRequest(BackendRequest request)
        {
            string text = request.payload.GetValue("text");
            if (text is null)
                return new BackendErrorResponse(-1, $"{ModuleName} missing 'text' parameter.");

            var result = _textClassifier.PredictSentiment(text);

            if (result is null)
                return new BackendErrorResponse(-1, $"{ModuleName} PredictSentiment returned null.");

            var response = new SentimentAnalysisResponse
            {
                is_positive          = result?.Prediction?[1] > 0.5f,
                positive_probability = result?.Prediction?[1]
            };

            return response;
        }
    }
}

连接一切

把所有东西都拼接在一起是微不足道的。在Program.cs 文件中

  • 更改行

services.AddHostedService<Worker>();

services.AddHostedService<SentimentAnalysisWorker>();

  • 通过添加以下行添加TextClassifier到DI容器

services.AddSingleton<TextClassifier>();

就在上一行之前。该文件应如下所示:

using SentimentAnalysis;

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddSingleton<TextClassifier>();
        services.AddHostedService<SentimentAnalysisWorker>();
    })
    .Build();

await host.RunAsync();

您需要使SentimentAnalysis成为前端项目的生成依赖项,以便在生成 CodeProject.AI Server 时生成它。

测试一下

为了测试这一点,我创建了一个简单的test.html页面,该页面采用一些文本,将其发送到 CodeProject.AI Server,然后处理和显示结果。这是我能做到的,以展示使用新功能是多么容易。

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title>Sentiment Analysis Module Test</title>
    <script type="text/javascript">

        function doAnalysis(textToSend) {
            var formData = new FormData();
            formData.append('text', textToSend);
            fetch('http://localhost:5000/v1/text/sentiment', {
                    method: 'POST',
                    body: formData,
                    cache: "no-cache"
                })
                .then(response => {
                    if (!response.ok) {
                        result.innerText = `Http error! Status : ${response.status}`;
                    }
                    return response.json().then(data => {
                        var resultHTML = data.is_positive
                                ? `<p>The text sentiment was positive 
                                with a probablity of ${data.positive_probability}</p>`
                                : `<p>The text sentiment was negative with a 
                                probablity of ${1.0 - data.positive_probability}</p>`;
                        result.innerHTML = resultHTML;
                    });
                });
        }
    </script>
</head>
<body>
    <h1>Sentiment Analysis Module Test</h1>
    <form method="post" action="" enctype="multipart/form-data" id="myform">
        <div>
            <label for="textToAnalyze">Text to analyze:</label>
            <div>
                <textarea id="textToAnalyze" name="textToAnalyze" 
                 rows="8" cols="80" style="border:solid thin black"></textarea>
            </div>        
        </div>
        <div>
            <button type="button" 
             onclick="doAnalysis(textToAnalyze.value)">Submit</button>
        </div>
        <br />
        <div>
            <label for="result">Result</label>
            <div id="result" name="result" style="border:solid thin black"></div>
        </div>
    </form>
</body>
</html>

若要查看此操作,请在调试器的调试配置中运行前端项目(CodeProject.AI服务器),然后在所选的浏览器中打开test.html文件。将一些文本复制到文本框中,然后按提交。我使用了亚马逊评论中的文字。您应该看到与此类似的内容:

附加奖励——在Windows上部署XCOPY模块

NET 6模块不像Python模块那样依赖于任何运行时的安装或虚拟环境的设置。NET 6运行时已由CodeProject.AI Server安装程序安装。因此,可以将内部版本的发布版本分箱部署到CodeProject.AI Server的现有Windows安装中。执行此操作的步骤如下:

  • 发布模式下生成模块项目。
  • c:\Program Files\CodeProject\AI\AnalysisLayer目录中创建一个文件夹。此目录的名称应与modulesettings.json文件中的FilePath目录同名。对于此示例,这将是“SentimentAnalysis”。
  • 将项目的bin\Release\net6.0目录的内容复制到在上一步中创建的目录。
  • 使用服务”应用重新启动CodeProject.AI Server服务。现在,新模块将在modulesettings.json文件中定义的终结点上公开。

总结

CodeProject.AI Server添加新模块通常并不困难。你需要

  1. 选择一个自包含的模块,该模块可以作为方法调用公开功能。
  2. AnalysisServices文件夹中创建一个项目来存放您的项目,然后复制代码
  3. 创建一个适配器,该适配器将在CodeProject.AI服务器和代码之间建立接口
  4. 确保模型和依赖项就位
  5. 创建一个modulesettings.json文件,向服务器描述如何启动模块
  6. 对要添加的模块进行任何需要的微小更改,以允许它与适配器一起运行,并修改Program.cs文件以使事情顺利进行

您的模块现在是CodeProject.AI生态系统的一部分,任何使用该服务器的客户端现在都可以无缝访问您的新模块。

https://www.codeproject.com/Articles/5334462/Adding-a-NET-AI-Module-to-CodeProject-AI-Server

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值