2023年的深度学习入门指南(13) - 写后端

news2024/11/25 15:34:07

2023年的深度学习入门指南(13) - 写后端

我们前面学习了用python在本机上写前端,也学习了使用HTML在本机写前端。同理,我们可以写Windows应用,mac应用,iOS应用,Android应用等等以适合各种终端。其实,最通用的终端就是Web前端。

为了使用Web前端,我们需要写后端,也就是服务端代码。

写后端可以有哪些好处?
首先,有了后端,我们就可以将数据存储到数据库里进行存储和进一步的分析。
其次,有了后端,我们可以接多个API提供方,提供1+1>2的效果。
第三,后端运行在服务器上,我们可以使用服务器的计算资源,而不是自己的电脑的计算资源,更加稳定。
最后,有了后端,我们就可以提供服务给其他小伙伴使用了。

基于开源项目修改自己的后端

chatgpt的生态如此丰富,我们有大量的开源项目可以参考。我们可以直接使用这些项目,当然多数情况肯定是要进行一些自己的修改,要不然直接用第三方的现成产品就好了。通过修改这些开源项目,可以大大加速上线时间,也方便满足我们的需求。

我们这里选用chatgpt-demo项目来作为讲解的例子,它的地址在:https://github.com/anse-app/chatgpt-demo。

运行chatgpt-demo

我个人选取它作为例子的原因是因为它是基于astro写的,代码看起来比较清爽。Astro是基于Node.js的,跟前端一样都是js,对于新同学入门的话比较友好。

我们首先clone这个项目:

git clone https://github.com/anse-app/chatgpt-demo

第二步,我们安装Node.js,可以直接从Nodejs.org下载安装包,也可以使用nvm来安装。nvm请参照:https://github.com/nvm-sh/nvm。

第三步,我们安装依赖:

npm install pnpm -g
pnpm install

pnpm的速度要比npm快很多,所以我们这里使用pnpm。

第四步,修改配置文件。

找到目录下的.env.example文件,将它复制一份并命名为.env,然后修改里面的配置。
最主要修改的有三个参数,其中只有第一个是强制要求的:

  • 第一个是OPENAI_API_KEY,这个是我们的OpenAI API Key
  • 第二个是HTTPS_PROXY,如果你需要使用代理的话,可以在这里设置,否则不用设置
  • 第三个是SITE_PASSWORD,这个是我们的密码,如果不设置的话,就是公开的,任何人都可以访问
# Your API Key for OpenAI
OPENAI_API_KEY=你的API Key
# Provide proxy for OpenAI API. e.g. http://127.0.0.1:7890
HTTPS_PROXY=
# Custom base url for OpenAI API. default: https://api.openai.com
OPENAI_API_BASE_URL=
# Inject analytics or other scripts before </head> of the page
HEAD_SCRIPTS=
# Secret string for the project. Use for generating signatures for API calls
PUBLIC_SECRET_KEY=
# Set password for site, support multiple password separated by comma. If not set, site will be public
SITE_PASSWORD=你的密码
# ID of the model to use. https://platform.openai.com/docs/api-reference/models/list
OPENAI_API_MODEL=gpt-4

第五步,运行:

pnpm start --host

注意,这里我们使用了–host参数,这样我们就可以在外网访问这个应用了。

输出结果如下:

> chatgpt-api-demo@0.0.1 start /root/code/chatgpt-demo
> astro dev "--host"

  🚀  astro  v2.1.3 started in 355ms
  
  ┃ Local    http://localhost:3000/
  ┃ Network  http://192.168.0.189:3000/

第六步,访问。

我们就可以访问我们的gpt应用了。如果你设置了密码,那么就需要输入密码才能访问。
地址参照你运行pnpm start --host的输出结果。比如我的就是:http://192.168.0.189:3000/

下面是我的运行结果,我修改了一点,跟你的可能不一样:

下面的官方的结果:

定制化自己的chatgpt-demo

下面我们就可以进行自己的定制了。选这个Astro工程的原因为就是修改起来很方便,比如首页的头部的代码,大家可以看到就是非常简单的几个标题字符串,大家想改成什么就改成什么。

---
import { model } from '../utils/openAI'
import Logo from './Logo.astro'
import Themetoggle from './Themetoggle.astro'
---

<header>
  <div class="fb items-center">
    <Logo />
    <Themetoggle />
  </div>
  <div class="fi mt-2">
    <span class="gpt-title">旭伦GPT</span>
    <span class="gpt-subtitle">1.0</span>
  </div>
  <p mt-1 op-60>Powered by OpenAI API ({model}).</p>
</header>

我们可以看到,这里的代码是astro的代码,它的语法跟HTML非常相似,但是它可以直接使用js,所以我们可以在这里写js代码。

我们来看主页的内容,基本上就是header, footer加上一段检查密码的代码:

---
import Layout from '../layouts/Layout.astro'
import Header from '../components/Header.astro'
import Footer from '../components/Footer.astro'
import Generator from '../components/Generator'
import '../message.css'
import 'katex/dist/katex.min.css'
import 'highlight.js/styles/atom-one-dark.css'
---

<Layout title="Xulun GPT">
  <main >
    <Header />
    <Generator client:load />
    <Footer />
  </main>
</Layout>

<script>
async function checkCurrentAuth() {
  const password = localStorage.getItem('pass')
  const response = await fetch('/api/auth', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      pass: password,
    }),
  })
  const responseJson = await response.json()
  if (responseJson.code !== 0)
    window.location.href = '/password'
}
checkCurrentAuth()
</script>

运行一下修改的,我的就变成了这样:

最后我们再看一下chatgpt-demo里面是如何调用openai的:

export const model = import.meta.env.OPENAI_API_MODEL || 'gpt-3.5-turbo'

export const generatePayload = (apiKey: string, messages: ChatMessage[]): RequestInit & { dispatcher?: any } => ({
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${apiKey}`,
  },
  method: 'POST',
  body: JSON.stringify({
    model,
    messages,
    temperature: 0.6,
    stream: true,
  }),
})

我们看到了stream: true,这是以流式的方式来访问openai的API.

接下来我们看来流是如何处理的:

  const stream = new ReadableStream({
    async start(controller) {
      const streamParser = (event: ParsedEvent | ReconnectInterval) => {
        if (event.type === 'event') {
          const data = event.data
          if (data === '[DONE]') {
            controller.close()
            return
          }
          try {
            const json = JSON.parse(data)
            const text = json.choices[0].delta?.content || ''
            const queue = encoder.encode(text)
            controller.enqueue(queue)
          } catch (e) {
            controller.error(e)
          }
        }
      }

      const parser = createParser(streamParser)
      for await (const chunk of rawResponse.body as any)
        parser.feed(decoder.decode(chunk))
    },
  })

这段代码创建了一个ReadableStream对象,它是用于处理流式数据的Web API。

在这段代码中,只使用了start方法,这个方法在流被构造或者需要提供数据时会被调用。start方法接收一个controller参数,这个参数是一个ReadableStreamDefaultController对象,它提供了enqueue、close和error等方法,可以用来控制流的状态。

解析事件流的工作是通过createParser方法创建的parser对象来完成的。这个parser对象会对服务器发送的每一块数据(chunk)进行解析,然后调用streamParser函数处理解析后的事件。

streamParser函数会检查事件的类型,如果事件类型为event,它就会提取出事件中的数据,然后尝试将数据解析为JSON格式,并提取出其中的文本信息。如果数据是[DONE],它就会关闭流。如果在解析或提取过程中出现错误,它就会调用controller.error方法并传入错误对象,使得流进入错误状态。

基本上我们可以理解为,如果一个chunk是[DONE],那么就关闭流,否则就把chunk解析成json,然后把json里面的delta.content字段的内容放入到流里面。

OK。现在需要什么功能,你就可以在这个工程的基础上修改了。
如果遇到无法连接之类的问题,可以参考这个:https://github.com/anse-app/chatgpt-demo/discussions/270

自己写后端

光用别人的东西,可能对于细节就缺失了一些了解。而且,主流的后端还是基于Spring Boot框架,使用Java或者是Kotlin来写的。

编译Spring Boot工程的话,我们需要maven或者是gradle这样的构建工具,它有中心仓库,可以自动下载依赖。写Spring Boot的话,我们最好有个趁手的IDE,比如IntelliJ IDEA或者是Visual Studio Code。

IDEA

写主类

我们都知道,Java应用需要一个主类,这个主类需要有main方法,这个main方法是程序的入口。

在Spring Boot应用里面,这个工作交给SpringApplication类来完成。我们的main方法如下:

package cn.lusing.chat.chat

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class ChatApplication

fun main(args: Array<String>) {
	runApplication<ChatApplication>(*args)
}

为了能让这个应用运行起来,我们需要写一个pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.0.6</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>cn.lusing.chat</groupId>
	<artifactId>chat</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>chat</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
		<kotlin.version>1.8.21</kotlin.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.module</groupId>
			<artifactId>jackson-module-kotlin</artifactId>
		</dependency>
		<dependency>
			<groupId>org.jetbrains.kotlin</groupId>
			<artifactId>kotlin-reflect</artifactId>
		</dependency>
		<dependency>
			<groupId>org.jetbrains.kotlin</groupId>
			<artifactId>kotlin-stdlib-jdk8</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib-jdk8</artifactId>
            <version>${kotlin.version}</version>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-test</artifactId>
            <version>${kotlin.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

	<build>
		<sourceDirectory>src/main/kotlin</sourceDirectory>
		<testSourceDirectory>src/test/kotlin</testSourceDirectory>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<executions>
					<execution>
						<goals>
							<!-- 生成可执行 JAR 包 -->
							<goal>repackage</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.jetbrains.kotlin</groupId>
				<artifactId>kotlin-maven-plugin</artifactId>
                <version>${kotlin.version}</version>
                <executions>
                    <execution>
                        <id>compile</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>test-compile</id>
                        <phase>test-compile</phase>
                        <goals>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
					<args>
						<arg>-Xjsr305=strict</arg>
					</args>
					<compilerPlugins>
						<plugin>spring</plugin>
					</compilerPlugins>
                    <jvmTarget>17</jvmTarget>
                </configuration>
				<dependencies>
					<dependency>
						<groupId>org.jetbrains.kotlin</groupId>
						<artifactId>kotlin-maven-allopen</artifactId>
						<version>${kotlin.version}</version>
					</dependency>
				</dependencies>
			</plugin>
		</plugins>
	</build>
</project>

有了这个xml文件,我们就可以使用maven来编译我们的工程了。

编译:

mvn compile

生成可执行jar包:

mvn package

然后运行:

java -jar target/chat-0.0.1-SNAPSHOT.jar

也可以直接在IDE里面运行。

运行起来的效果是像这样的:


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.0.6)

2023-05-13T01:32:16.879+08:00  INFO 5925 --- [           main] cn.lusing.chat.chat.ChatApplicationKt    : Starting ChatApplicationKt using Java 17.0.6 with PID 5925 (/Users/liuziying/working/misc/java/chat/chat/target/classes started by liuziying in /Users/liuziying/working/misc/java/chat)
2023-05-13T01:32:16.884+08:00  INFO 5925 --- [           main] cn.lusing.chat.chat.ChatApplicationKt    : No active profile set, falling back to 1 default profile: "default"
2023-05-13T01:32:17.868+08:00  INFO 5925 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-05-13T01:32:17.879+08:00  INFO 5925 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-05-13T01:32:17.880+08:00  INFO 5925 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.8]
2023-05-13T01:32:17.984+08:00  INFO 5925 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-05-13T01:32:17.986+08:00  INFO 5925 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1044 ms
2023-05-13T01:32:18.240+08:00  INFO 5925 --- [           main] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page: class path resource [static/index.html]
2023-05-13T01:32:18.373+08:00  INFO 5925 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-05-13T01:32:18.383+08:00  INFO 5925 --- [           main] cn.lusing.chat.chat.ChatApplicationKt    : Started ChatApplicationKt in 1.905 seconds (process running for 2.373)

写一个处理Rest API的类

Rest是Resource Representational State Transfer的缩写,直译为表现层状态转移,是一种软件架构风格,用于设计和实现Web服务。

Rest的核心思想是将网络上的资源用统一资源标识符(URI)来表示,并通过HTTP协议提供的方法(如GET、POST、PUT、DELETE等)来对资源进行操作。

Rest的优点是简化了接口的设计,提高了可读性和可维护性,适用于各种客户端和平台。

我们的服务端代码就要基于Rest API来提供服务。

我们先写一个最简单的例子让大家理解一下Rest API的工作方式:

package cn.lusing.chat.chat
import org.springframework.web.bind.annotation.*

@CrossOrigin(origins = ["*"])
@RestController
class MainController{
    @RequestMapping("/api/v1/chat/{message}")
    fun hello(@PathVariable(name="message") message : String) : String {
        return "Hello,Chat!$message";
    }

    @PostMapping("/api/v1/chat2")
    fun hello2(@RequestBody json: String) : String {
        return "{'data':'$json'}";
    }
}

我们以一个get请求和一个post请求为例,给大家讲解下Rest API是如何工作的。

我们先来看get请求,这个请求的地址是:http://localhost:8080/api/v1/chat/chatgpt。

我们通过浏览器访问这个地址,可以看到返回的结果是:Hello,Chat!chatgpt。其中,chatgpt就是我们传入的参数,大家可以换一个试试效果。

但是,在URL里面传递参数,有时候是不方便的,比如我们要传递一个很长的文本,这个文本可能会超过URL的长度限制。这个时候,我们就需要使用post请求。

Post请求就无法将参数放在URL里面了,我们需要把参数放在请求的body里面。我们可以使用curl命令来测试一下:

curl -X POST http://127.0.0.1:8080/api/v1/chat2 -d '{message:"aaa"}'

返回值如下:

{'data':'%7Bmessage%3A%22aaa%22%7D='}%

我们可以看到,我们传入的参数是{message:“aaa”},但是返回的结果是%7Bmessage%3A%22aaa%22%7D=。这是因为我们传入的参数是json格式的,而返回的结果是url编码的格式。

写首页

我们的首页就是一个html文件,我们可以直接把它放在resources/static/index.html里面。

<!DOCTYPE html>
<html>
<head>
    <title>智能问答系统 Powered by chatgpt</title>
    <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="p-6 bg-gray-200">
    <h1 class="text-3xl mb-4">Chat Interface</h1>
    <input type="text" id="message" placeholder="Type your message here" class="px-4 py-2 mb-4 w-full border-2 border-gray-300 rounded">
    <button id="send" class="px-4 py-2 bg-blue-500 text-white rounded">Send</button>
    <div id="response" class="mt-4 p-4 border-2 border-gray-300 rounded"></div>

    <script>
        document.querySelector("#send").addEventListener('click', async () => {
            const message = document.querySelector("#message").value;
            try {
                const response = await fetch('/api/v1/chat2', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ message })
                });
                const data = await response.text();
                document.querySelector("#response").textContent = data;
            } catch (error) {
                console.error('Error:', error);
            }
        });
    </script>
</body>
</html>

我们可以看到,这个页面有一个输入框,一个按钮,一个输出框。我们在输入框里面输入内容,然后点击按钮,就可以把输入的内容发送到后端,然后后端返回一个结果,这个结果就显示在输出框里面。

运行的效果如下:

调用openai API

下面我们就剩最后一道工序了,把用户的输入传给openai,然后把openai的结果返回给用户:

package cn.lusing.chat.chat

import java.io.OutputStreamWriter
import java.net.HttpURLConnection
import java.net.URL

fun chatWithOpenAI(apiKey: String, message: String): String {
    val url = URL("https://api.openai.com/v1/chat/completions")
    with(url.openConnection() as HttpURLConnection) {
        requestMethod = "POST" // 设置请求类型为 POST

        // 设置请求头
        setRequestProperty("Content-Type", "application/json")
        setRequestProperty("Authorization", "Bearer $apiKey")

        // 设置请求体
        val body = """
            {
                "model": "gpt-3.5-turbo",
                "messages": [
                    {
                        "role": "system",
                        "content": "You are a helpful assistant."
                    },
                    {
                        "role": "user",
                        "content": "$message"
                    }
                ]
            }
        """.trimIndent()

        doOutput = true
        OutputStreamWriter(outputStream).use {
            it.write(body)
        }

        // 返回响应
        return inputStream.bufferedReader().use { it.readText() }
    }
}

我们修改一下Controller的方法:

    @PostMapping("/api/v1/chat2")
    fun hello2(@RequestBody json: String): String {
        val jsonNode = objectMapper.readTree(json)
        val message = jsonNode.get("message")?.asText()
        val apiKey = "你的key"
        return chatWithOpenAI(apiKey, "$message");
    }

我们来看下效果:

链路已经通了。

后面调调格式,首先把data改成data?.choices[0]?.message?.content:

        document.querySelector("#send").addEventListener('click', async () => {
            const message = document.querySelector("#message").value;
            try {
                const response = await fetch('/api/v1/chat2', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ message })
                });
                const data = await response.json();
                document.querySelector("#response").textContent = data?.choices[0]?.message?.content;
            } catch (error) {
                console.error('Error:', error);
            }
        });

再给response div改个增加个pre的样式:

<div id="response" class="mt-4 p-4 border-2 border-gray-300 rounded" style="white-space: pre;"></div>

效果如下:

代码格式没有highlight,我们之前在前端的时候搞过了,这里就不再重复了。

小结

恭喜,从此您解锁了调用大模型的后端能力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/519673.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python+chrome rpc方式轻松绕过五秒盾(cloudflare)

Cloudflare 5秒盾是一种基于云技术的Web应用程序防火墙(WAF),旨在保护网站免受各种Web攻击,如SQL注入、跨站点脚本(XSS)和DDoS攻击。它能够在5秒内检测到并阻止恶意流量,并提供实时安全警报和日志记录。此外,它还提供了一系列安全功能,包括SSL / TLS加密、IP过滤、访问…

单链表你别再找我了,我怕双向链表误会

目录 带头双向循环链表的创建和初始化 创建一个新的结点&#xff08;方便复用&#xff09; 链表判空 链表打印 链表尾插 链表尾删 链表头插 链表头删 任意插入 任意删除 链表查找 链表销毁 完整代码 &#x1f60e;前言 之前我们讲了结构最简单&#xff0c;实现起来…

kettle win11 启动闪退 --启动日志

一、启动闪退 思路&#xff1a; 找原因找启动日志根据启动日志查看启动失败的原因 二、找启动日志 采用debug模式启动 查看控制台–根据控制台操作 看生成的启动日志文件 查看日志 DEBUG: Using PENTAHO_JAVA_HOME DEBUG: _PENTAHO_JAVA_HOMEE:\java8 DEBUG: _PENTAHO…

记一次靶场搭建与渗透测试

渗透目标 通过Windows7打入工作组环境&#xff0c;穿透两层内网拿到DC&#xff08;域控制器&#xff09;权限 环境搭建 环境搭建 网络拓扑 虚拟机网络配置 渗透测试 永恒之蓝外网打点 nmap -sS 192.168.2.0/24扫描外网存活主机&#xff0c;发现两台主机192.168.2.128和192…

芯片电源附近为什么放置的是0.1uF电容

日常使用情况 我们在电源滤波电路上可以看到各种各样的电容&#xff0c;100uF、10uF、100nF、10nF不同的容值&#xff0c;而在我们使用中常常会在芯片电源附近放置0.1uF电容&#xff0c;以TB67S109AFNG应用手册为例&#xff0c;其中推荐使用的也是0.1uF的电容 电容的特性 数字…

分享几款小白从零开始学习的会用到的工具/网站

大二狗接触编程也有两年了&#xff0c;差生文具多这大众都认可的一句话&#xff0c;在这里蹭一下这个活动分享一下从0开始学习编程有啥好用的工具 目录 伴侣一、Snipaste截图工具 伴侣二、Postman软件&#xff08;可用ApiPost平替&#xff09; 伴侣三、字体图标网站 伴侣四…

BlockChain-Account_TakeOver

题目描述 ECDSA 签名 假设我们的私钥为 d A d_A dA​而公钥为 Q A Q_A QA​&#xff0c; Q A d A ⋅ G Q_Ad_A\cdot G QA​dA​⋅G&#xff0c;接下来就是签名的过程&#xff0c;要签名的消息为 m m m 取 e H A S H ( m ) e HASH(m) eHASH(m)取 e e e的左边的 L n L_n L…

Baumer工业相机堡盟工业相机软件CameraExplorer常见功能使用说明二

Baumer工业相机堡盟工业相机软件CameraExplorer常见功能使用说明二 Baumer工业相机Baumer工业相机CE软件图像/视频存储功能Baumer工业相机CE软件记录日志文件功能Baumer工业相机CE软件查看图像Buffer及数据流统计信息 Baumer工业相机 Baumer工业相机堡盟相机是一种高性能、高质…

汇编五、伪指令与汇编程序结构

1、伪指令 1.1、概念 (1)伪指令是用于对汇编过程进行控制的指令&#xff0c;该类指令并不是可执行指令&#xff0c;没有对应机器码&#xff0c;只用于汇编过程中为汇编程序提供汇编信息&#xff0c;帮助编译器编译。 1.2、ASM51提供的伪指令 伪指令分为如下几类。 1.2.1、…

zuul源码分析

zuul源码解析 zuul与springboot整合的依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-zuul</artifactId></dependency>看到starter第一反应就是springboot的自动装配? 我们去…

简历制作、投递、(实习)面试技巧!!

目录 1.为什么要找实习 2.什么时候找实习 3.制作简历 4.简历注意事项 5.IT后端的校招的要求 6.简历里面写什么&#xff1f; 7.简历模板的选择 8.个人信息 9.求职意向 10.专业技能 11.项目经验 12.其他注意事项 13.找工作的手段 14.找工作的态度 ​编辑 15.面试…

IntelliJ IDEA 修改内存大小

idea有个配置文件&#xff0c;可以设置内存大小的&#xff0c;就跟咱的jvm的内存里面的堆大小&#xff0c;栈大小等等&#xff0c;可以设置的&#xff0c;而且设置了之后&#xff0c;你这个的性能就会得到提升。具体看下面怎么修改。 先说所要修改的文件 idea.vmoptions 的位置…

总结常见评价指标

整理一下在机器学习中常见的评价指标&#xff0c;包括&#xff1a; 混淆矩阵&#xff0c;TPR&#xff0c;FPR&#xff0c;TNR&#xff0c;FNR&#xff1b;Precision&#xff0c;Recall&#xff0c;Accuracy&#xff0c;F-score(F1-meature)ROC曲线&#xff0c;AUC&#xff1b; …

[Vue warn]: You may have an infinite update loop in a component render function

老板让该一个bug&#xff0c;结果一连出现好几个问题&#xff0c;然后报错也是很奇葩&#xff0c;在源代码上不能直接定位到&#xff0c;只知道在当前页面上出现的问题&#xff0c;弄了好久&#xff0c;给大家分享一下解决的经验&#xff1a; You may have an infinite update …

2023 年Windows MySql 5.7,MySql 8.0 下载安装教程, 附详细图解

文章目录 下载 MySQL 安装程序安装 MySQL 数据库安装示例数据库连接到 MySQL 服务器 在本教程中&#xff0c;我们展示如何在 Windows 平台上下载和安装 MySQL 的详细步骤。 在 Windows 平台上安装 MySQL 很简单&#xff0c;并不需要太复杂的步骤。按照本文的步骤操练起来就可以…

【Win10错误】从80190001错误码恢复

目录 一、说明 二、操作过程和错误显示 三、修复过程 四、网上的其它参考意见 一、说明 出现0x80190001错误码&#xff0c;其原因是网络认证问题引起。但不是网络断开或路由不通而引起。一般是本地身份cooki无法认证而引起&#xff0c;一般出现在登录认证过程。本篇告诉大家…

2.4G无线游戏手柄方案开发

对于游戏玩家来说&#xff0c;好的外设才能有更好的游戏体验。相比于传统的有线手柄&#xff0c;2.4G无线游戏手柄采用2.4GHz射频无线连接方式&#xff0c;摆脱了连线的困扰。相比于鼠标键盘&#xff0c;游戏手柄在大部分游戏上的使用体验都会更好&#xff0c;让你的游戏体验更…

【MATLAB第30期】基于MATLAB的adaboost多分类预测集成学习模型(四种模型GDA高斯判别分析、Knn、NB朴素贝叶斯、SVM)

【MATLAB第30期】基于MATLAB的adaboost多分类预测集成学习模型&#xff08;四种模型GDA高斯判别分析、Knn、NB朴素贝叶斯、SVM&#xff09; 一、简介 弱分类器 %1.GDA高斯判别分析 %2.Knn (NumNeighbors 5) K邻近 %3.Naive Bayes 朴素贝叶斯 %4.SVM 支持向量机 强分类器 1.a…

【Vue】生命周期

文章目录 生命周期概念一、生命周期图示二、生命周期1.beforeCreate&#xff08;&#xff09;{}2.created&#xff08;&#xff09;{}3.beforeMount&#xff08;&#xff09;{}4.mounted&#xff08;&#xff09;{}5.beforeUpdate&#xff08;&#xff09;{}6.updated&#xff…

C语言预处理详解

参考文章&#xff1a;c语言预处理 目录 程序的翻译环境和执行条件 翻译环境 编译本身也分为几个阶段 预处理 预处理指令 运行环境 程序执行的过程 预处理 预定义符号 #define #define定义标识符 #define定义宏 宏的申明方式 #define替换规则 #和## #的作用 ##…