0%

相比fetch Api, axios 提供了更简洁、更直观的 API,使得发送 HTTP 请求和处理响应更加方便; 自动处理json对象(fetch 可以调用await response.json())

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
axios.post('/api/submit', this.editForm)
.then(response => {
console.log('Form submitted successfully:', response.data);
})
.catch(error => {
console.error('Error submitting form:', error);
});

axios.request({
url: '/api/submit',
method: 'post',
data: this.editForm
}).then(response => {
console.log('Form submitted successfully:', response.data);
}).catch(error => {
console.error('Error submitting form:', error);
});

instance
1
2
3
4
5
6
7
const instance = axios.create({ baseURL: '/api' });

// Works just like axios(config)
instance({
url: '/users',
method: 'get'
});

拦截器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
},
{ synchronous: true, runWhen: () => /* This function returns true */}
);

// Add a response interceptor
axios.interceptors.response.use(function onFulfilled(response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
}, function onRejected(error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
});

安装依赖

1
2
3
4
<ItemGroup>
<PackageReference Include="HandyControl" Version="3.5.1" />
<PackageReference Include="Prism.Unity" Version="8.1.97" />
</ItemGroup>

app.xmal中Wpf:Application替换成prism:PrismApplication
1
2
3
4
5
6
7
8
9
<prism:PrismApplication
x:Class="WpfApp1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1"
xmlns:prism="http://prismlibrary.com/">
<Application.Resources>
</Application.Resources>
</prism:PrismApplication>

标记中删除了StartupUri=”MainWindow.xaml”(否则生成两个实例)

cs代码实现两个抽象方法RegisterTypes和CreateShell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 创建程序主窗口
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}

protected override void RegisterTypes(IContainerRegistry container)
{
// 注册导航页面和ViewModel
container.RegisterForNavigation<MainWindow, MainWindowViewModel>();

// 注册单例服务
container.RegisterSingleton<IMyService, MyService>();

// 配置文件基础路径
string programData = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
programData = Path.Combine(programData, "xxxx");

// 从注册表读取配置路径
using (var key = Registry.LocalMachine.OpenSubKey($@"SOFTWARE\xxxx"))
{
if (key != null && key.GetValue("SolutionPath") != null)
{
programData = key.GetValue("SolutionPath").ToString();
}
}

// 初始化全局缓存
GlobalCache.Instance.FileBasePath = programData;
GlobalCache.Instance.InitPaths();
}

init scene

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const canvas = document.createElement('canvas')
const sizes = {
width: this.$refs.container.clientWidth,
height: this.$refs.container.clientHeight
}
canvas.width = sizes.width;
canvas.height = sizes.height;
this.$refs.container.appendChild(canvas);
// 创建3D场景对象Scene
const scene = new THREE.Scene();
console.log(scene);
// Geometry
const geometry = new THREE.SphereGeometry(5, 32, 32);
const material = new THREE.MeshBasicMaterial({color: '#aaa', wireframe:true})
// Mesh
const mesh = new THREE.Mesh(geometry, material)
mesh.position.set(0, 0, -10);
scene.add(mesh)

// 创建相机对象Camera

const camera = new THREE.PerspectiveCamera(75, sizes.width/sizes.height, 0.1, 1000);
scene.add(camera);

const renderer = new THREE.WebGLRenderer({canvas: canvas});
renderer.render(scene, camera);

向量点乘

内积 数量积

$\vec{a}$·$\vec{b}$ = $x_1 x_2$ + $y_1y_2$ + $z_1z_2$= |$\vec{a}$||$\vec{b}$|cosθ

标量结果 一个向量在另一个向量方向的投影与被投影向量模的乘积

几何意义 对于单位向量 点乘结果取决于向量方向夹角 反映了方向的一致性

向量叉乘

外积 向量积

名字由来是坐标分量做交叉相乘然后相减的操作

方向垂直与向量所在平面 符合右手法则

几何意义 叉乘结果的正负可以反映方向关系
$\vec{a}$ × $\vec{b}$ > 0 ,$\vec{a}$ 在 $\vec{b}$ 右侧

$\vec{a}$ × $\vec{b}$ < 0 ,$\vec{a}$ 在 $\vec{b}$ 左侧

Godot是C++开发的基于opengl的游戏引擎,MIT开源协议,完全免费。支持一键导出到多个平台,包括主流的桌面平台(Linux、macOS、Windows)、移动平台(Android、iOS)、基于 Web 的平台以及主机平台。

Godot Docs

Token

Token 是大型语言模型处理文本的基本单位。不同模型采用不同的分词方式(分词器),当我们将一段文字输入模型时,模型会先将其拆解成 Token 序列,然后通过这些序列进行预测。

分词器的具体执行原理较为复杂。我们可以简化地将分词器视为一个字典,对于字典中存在的内容,按照字典进行切分并替换为对应的数字
Hello 你好呀 —> Hello | _ | 你好 | 呀 (character: 9, token: 4)

神经网络结构

经典的卷积神经网络(CNN)结构包括以下层:

输入层→卷积层→池化层→全连接层

例如,假设要对0-9十个数字的图片进行分类,则全连接层的输出维度通常要设置为10,与输出分类一一对应。最后通过Softmax层,程序就可以将输出转换为每个类别的概率,概率最高的类别即为预测结果

理解了CNN的输出层维度与分类任务的关系,我们也可以将这个概念迁移到对Transformer语言模型的理解上。尽管二者架构迥异,但是它们在输出层维度及分类任务的原理上仍有共性。

语言模型本质上仍然是一种预测模型,它的核心目标,就是预测给定上下文的下一token。输入层在复杂的网络中传播,最终到达输出层,并且形成 N 个维度的输出。而每个维度正对应着分词表中的一个token。

例如,如果分词表包含50000个token,那么Transformer模型的输出层将输出一个50000维的向量,向量中的每个元素代表对应token的概率。模型会选择概率最高的token作为预测的下一个token。

早期llm的词表(Vocabulary)规模非常小,对应的输出维度也很小,因此相同环境下的理论计算性能更优。但是词表中没有出现的罕见词,有可能被拆分成子词甚至字符。就如ChatGPT 4.0及以前的Vocabulary,大部分中文字符,都会被拆分为2-3个子词token。显然,小规模的Vocabulary有更低的信息密度,相同的输入输出会占据更大的序列长度,这对于输入与输出都有很大的负面影响。

以Llama2-7B模型为例,当词表从32k扩展至64k时,理论的嵌入矩阵参数量从32,000×4096增至65,536×4096,但平均序列长度缩短约30%,实际运行中的整体推理速度提升了15%。

Transformer

Transformer语言模型是基于自注意力机制构建的深度学习架构,已在自然语言处理领域引发革命性变革。
核心架构:

  • 自注意力机制:通过计算序列中每个元素与其他元素的相关性权重(Q/K/V矩阵),捕捉长距离依赖关系。相比RNN/CNN,并行计算效率提升3-8倍(论文7、10)。

  • 多头注意力:将输入切分为多个子空间并行处理,可同时学习语法、语义等不同特征维度(论文3、12)。

  • 位置编码:采用正弦函数生成绝对位置编码(论文1)或相对位置编码(论文18),解决序列顺序建模问题。

它的核心目标,就是预测给定上下文的下一token。

这意味着,Transformer语言模型每输出一个token,输入数据便在神经网络经过了一次完整的传播。最终从词表查询到token string,展示在你眼前的,无论是一个字母,一个单词,甚至一个短语,从模型的角度来看,它们都是“平等的”——从模型角度来看,它们都是一个独立的预测单元,最终对应到词表中的一个 token。

从输入角度上看,正如我们之前提到的,transformer的自注意力模型会将上下文加载到注意力矩阵中,上下文token数直接决定了矩阵的规模,由于O(n²)的时间复杂度,以及O(n²d)的空间复杂度(d为隐藏层维度),小规模词表所带来的低密度信息,可能反而削弱计算性能,增加资源消耗。

从输出角度上看,更小的词表同样意味着单位token包含的信息更少,完成同样的一句输出将会经过更多的推理次数。

机器视觉是人工智能除LLM外的另一个分支

一、传统机器学习模型
支持向量机(SVM)

应用案例:近红外光谱识别(6个特征波长输入,95.3%准确率)

优势:小样本表现优异,适合光谱数据

缺陷:依赖特征工程,需人工提取菌盖颜色、形状等特征

随机森林(RF)

应用案例:15特征(形态+颜色+纹理)分类,全特征建模最优

效果:预测精度超SVM,适合结构化特征数据

二、深度学习模型
基础CNN架构

典型表现:传统CNN在3,100张测试集上首图命中率48.7%

改进方案:降梯度卷积(扩大感受野),命中率提升至81.7%

经典网络改进

ResNet50:迁移学习+图像增强后,测试集平均识别时间0.985秒

EfficientNet:在49,958张自建数据集上,宏F值提升0.75%

MobileNetV2:配合权重衰减策略(YWeight),top-1准确率提升0.87%

细粒度识别专用

双线性CNN:结合Inception-ResNet-v2,在菌盖纹理相似度>85%的数据集上,细粒度分类准确率超95%

Xception模型:迁移学习+多场景数据增强,野外复杂背景识别率达96.67%

轻量化模型

Micro V2:苹果M1 CPU训练142秒达88%准确率,模型体积仅3.5MB

ShuffleNetV2:在8,375张增强数据上,识别速度较VGG16提升3倍

1
npx expo install expo-auth-session expo-crypto # expo-crypto 是 peer dependency 须与 expo-auth-session 一起安装

基于web浏览器的认证

app.json中配置用于重定向携带参数的scheme

1
2
3
4
5
6
7
8
{
"expo": {
"name": "My App",
"slug": "my-app",
"scheme": "myapp", // 这里定义了应用的 scheme
...
}
}

scheme 字段用于定义应用的 URL scheme。URL scheme 是一种协议,允许其他应用程序通过特定的 URL 格式打开你的应用,并传递数据给它。该协议是实现深度链接(deep linking)和通用链接(universal linking)的基础。

深度链接(Deep Linking)和通用链接(Universal Links)是两种用于直接导航到移动应用内特定内容或功能的技术

深度链接是一种允许应用程序内部特定页面或功能被直接访问的链接形式。通过使用自定义URL方案(Custom URL Scheme),开发者可以创建指向应用内任何位置的链接,形如myapp://product/12345

通用链接是苹果公司在iOS 9中引入的一种技术,它使得HTTP/HTTPS链接可以直接打开已安装的应用程序中的相应内容,链接形式与传统网站链接相同,不需要特殊的URL scheme(不依赖是否安装,可避免不同应用程序使用相同的URL scheme的安全问题)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import { useState } from 'react';
import * as WebBrowser from 'expo-web-browser';
import {
exchangeCodeAsync,
makeRedirectUri,
useAuthRequest,
useAutoDiscovery,
} from 'expo-auth-session';
import { Button, Text, SafeAreaView } from 'react-native';


WebBrowser.maybeCompleteAuthSession();


export default function App() {
// Endpoint
const discovery = useAutoDiscovery(
'https://login.microsoftonline.com/<TENANT_ID>/v2.0',
);
const redirectUri = makeRedirectUri({
scheme: 'com.app',
path: 'auth',
});
// 认证成功返回以下uri
// Development Build: my-scheme://redirect
// Expo Go: exp://127.0.0.1:8081/--/redirect
const clientId = '<CLIENT_ID>';

// We store the JWT in here
const [token, setToken] = useState<string | null>(null);

// Request
const [request, , promptAsync] = useAuthRequest(
{
clientId,
scopes: ['openid', 'profile', 'email', 'offline_access'],
redirectUri,
},
discovery,
);

return (
<SafeAreaView>
<Button
disabled={!request}
title="Login"
onPress={() => {
promptAsync().then((codeResponse) => {
if (request && codeResponse?.type === 'success' && discovery) {
exchangeCodeAsync(
{
clientId,
code: codeResponse.params.code,
extraParams: request.codeVerifier
? { code_verifier: request.codeVerifier }
: undefined,
redirectUri,
},
discovery,
).then((res) => {
setToken(res.accessToken);
});
}
});
}}
/>
<Text>{token}</Text>
</SafeAreaView>
);
}

  • useAutoDiscovery(‘[OpenId issuer URL]’)