>

简单了解向量搜索

前言 最近看了篇文章,了解到了Vector Search。什么是Vector Search?来看看GPT的解释: 向量搜索是一种利用向量表示数据,并基于向量之间的相似性进行搜索的技术。它不同于传统的基于关键词的搜索,能够捕捉数据之间更深层次的语义关系,从而提高搜索的准确性和效率。 相较于基于关键词的搜索,将文本、图片等非数值型数据转换为向量形式,更能发现数据的特征,并发现数据之间的相似性。 至于如何将非数值型数据转换为向量,来开下面这个例子: 用“1”表示“是”,用“0”表示“否” 交通工具 轮子数量 有引擎吗 陆地上运动吗 最大承载人数 汽车 $4$ $1$ $1$ $4$ 自行车 $2$ $0$ $1$ $1$ 三轮车 $3$ $0$ $1$ $1$ 摩托车 $2$ $1$ $1$ $1$ 帆船 $0$ $0$ $0$ $20$ 轮船 $0$ $0$ $0$ $1000$ 把非数值型数据转换为向量,实际上就是把这些数据的特征用数值表示。比如对于汽车来说,它在这个例子中对应的向量就是$(4,1,1,4)$。 基础理论 向量之间的相似性 观察向量$\vec{a}$,$\vec{b}$和$\vec{p}$,哪个向量与$\vec{p}$更“相似”?向量$\vec{a}$与$\vec{p}$有着相同的方向,但模长更小;而$\vec{b}$与$\vec{p}$有着相同的模长,但方向只是相近。如果相似指的是方向上的相似,那么$\vec{a}$与$\vec{p}$更相似。而如果相似指的是模长,那么$\vec{b}$与$\vec{p}$更相似。 在矢量搜索中,很少单独根据模长来判断两个向量是否相似。相似性通常取决于方向,或者方向和模长。 相似性的评估方法 评估两个向量是否相似,通常有四种数学方法: 欧式距离:直接计算两个向量“箭头”的直线距离。 $d(\vec{a}, \vec{p}) = \sqrt{\sum_{i=1}^{n} (a_i - p_i)^2}$ $a_i$表示$\vec{a}$的第$i$个分量 曼哈顿距离:计算两个点之间沿着坐标轴行走的距离。 $d_1(\vec{a}, \vec{p}) = \sum_{i=1}^{n} |a_i - p_i|$ $a_i$表示$\vec{a}$的第$i$个分量 点积:将相同维度的分量相乘再相加。 $\vec{a} \cdot \vec{p} = \sum_{i=1}^{n}(a_i b_i)$ 余弦值:两个向量夹角的、余弦值。 $\cos(\theta) = \frac{\vec{a} \cdot \vec{b}}{|\vec{a}| |\vec{b}|}$ 这四种方法中,欧氏距离是最常用的,而曼哈顿距离与欧氏距离相似,应用这两种方法得到的结果通常也相似。而当需要评估相似性的向量模长一致时,使用余弦值是更好的方法,因为它只与夹角相关。点积与余弦值类似,但在需要大量计算的情况下,点积通常有着更好的性能。 简单的例子 由于在屏幕上呈现二维向量较为方便,因此选取前言中的“轮子数量”与“有引擎吗”作为特征。得到4个向量,分别是$(4,1),(2,0),(3,0),(2,1)$,它们在图上呈现为: 随后分别计算摩托车与自行车、三轮车、汽车的相似性:...

September 8, 2024 · 2 min

制作简单Magisk模块:音量阶数30阶

前言 手机默认音量阶数只有15阶,经常出现加一阶太大,减一阶又太小的情况。为了解决这个问题,可以制作一个Magisk模块,将音量阶数细分到30阶。以此来解决上述问题。 简单Magisk模块 Magisk模块的结构 为了将音量阶数修改为30阶,需要的东西其实非常少: MoudleName.zip │ ├── 📁META-INF │ └── 📁com │ └── 📁google │ └── 📁android │ ├── 📄update-binary │ └── 📄updater-script │ ├── 📄module.prop ├── 📄system.prop │ META-INF:Magisk官方提供,无需更改。 module.prop:模块信息。 system.prop:用来修改系统的build.prop。 module.prop的作用 module.prop id=<string> name=<string> version=<string> versionCode=<int> author=<string> description=<string> 名称 说明 id 模块唯一标识符 version 模块版本号 versionCode 模块版本标识,必须为整数 author 模块作者 description 模块描述 system.prop的作用 为了修改音量阶数为30阶,需要借由system.prop修改build.prop。system.prop中的配置会被替换到build.prop中的对应配置。 system.prop ro.config.media_vol_steps=29 ro.config.vc_call_vol_steps=29 ro.config.alarm_vol_steps=29 ro.config.system_vol_steps=29 ro.config.media_vol_default=10 // 开机时默认的音量大小,10表示10阶 小结 将打包成zip的模块安装到手机后重启,音量阶数便会被修改为30阶,并且初始音量大小为最大音量的1/3。经过测试,本人的手机(Redmi Note 12 Turbo, HyperOS)最多将音量阶数调整为30阶,超过这个数,修改就会无效。 更多关于Magisk模块的信息 一个功能丰富的Magisk模块的结构 module....

September 6, 2024 · 1 min

MSP430之时钟频率设置

写完这篇文章过了两天我才知道原来把官方例程下载下来可以用图形界面设置时钟。我是🐷 简介 MSP430有三个时钟,分别是MCLK、SMCLK、ACLK。 MCLK:主系统时钟。MCLK是CPU的运行时钟,MCLK频率越高,CPU运行越快,同时功耗越高。 SMCLK:子系统时钟。SMCLK可以提供给需要高速时钟的外设(如ADC、定时器。 ACLK:辅助时钟。辅助时钟的频率一般很低,功耗小,可以一直开启。 这三个时钟可由五个时钟源经过分频后提供,分别是XT1CLK、XT2CLK、VLOCLK、REFCLK、DCOCL。 XT1CLK(低频/高频时钟源):XT1CLK可以作为内部FLL模块的参考时钟,且它是FLL模块(频锁环模块)的默认参考时钟。FLL时钟除锁频之外还可以对时钟进行倍频或分频。 XT2CLK(高频时钟源):XT2CLK可以作为FLL的内部参考时钟。 VLOCLK(低频内部时钟源) REFCLK(低频时钟源,内部的参考时钟源):可以作为内部FLL的参考时钟。 DCOCLK(片内数字控制时钟源):通过内部FLL模块稳定,一般为1MHz。 其中XT1CLK、XT2CLK可由外部晶振提供。片内时钟源受温度影响大,频率一般不会很准确。 设置MSP430F5529的时钟 目的是将MCLK、SMCLK设置为XT2CLK(外部晶振 4Mhz)经过倍频后得到的24Mhz时钟;将ACLK设置为XT1CLK(外部晶振 32.768Khz)。 芯片的默认电压低,不足以支持高速的时钟频率,所以首先提升电压: PMM_setVCore(PMM_CORE_LEVEL_3); 设置XT1CLK与XT2CLK引脚为复用功能: GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P5,GPIO_PIN4 + GPIO_PIN5); GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P5,GPIO_PIN2 + GPIO_PIN3); 设置XT1CLK与XT2CLK外接晶振的频率: UCS_setExternalClockSource(32768,4000000); 开启XT1CLK与XT2CLK: UCS_turnOnLFXT1WithTimeout(UCS_XT1_DRIVE_0,UCS_XCAP_3,50000); UCS_turnOnXT2WithTimeout(UCS_XT2_DRIVE_4MHZ_8MHZ,50000); 将XT1CLK作为ACLK时钟源,XT2CLK作为FLL参考时钟源: UCS_initClockSignal(UCS_ACLK,UCS_XT1CLK_SELECT,UCS_CLOCK_DIVIDER_1) UCS_initClockSignal(UCS_FLLREF,UCS_XT2CLK_SELECT,UCS_CLOCK_DIVIDER_1); 提升MCLK的频率: UCS_initFLLSettle(24000, 6); // 6*4Mhz = 24Mhz 最后开启时钟源中断: SFR_clearInterrupt(SFR_OSCILLATOR_FAULT_INTERRUPT); SFR_enableInterrupt(SFR_OSCILLATOR_FAULT_INTERRUPT); 但我还有一个疑问,就是为什么不用使用UCS_initClockSignal函数将XT2CLK作为MCLK和SMCLK的时钟源,或者说将FLL参考时钟源设置为MCLK和SMCLK的时钟。我不这么做MCLK和SMCLK照样会被设置为XT2CLK倍频后的时钟。如果有大神知道原因,希望能留评论告诉我。 完整代码 clock.h #ifndef __CLOCK_H__ #define __CLOCK_H__ #include "driverlib.h" #define UCS_XT1_TIMEOUT 50000 #define UCS_XT2_TIMEOUT 50000 #define UCS_XT1_CRYSTAL_FREQUENCY 32768 #define UCS_XT2_CRYSTAL_FREQUENCY 4000000 #define UCS_MCLK_DESIRED_FREQUENCY_IN_KHZ 24000 #define UCS_MCLK_FLLREF_RATIO 6 void Clock_Init(void); #endif clock....

July 12, 2024 · 1 min

PCA9685舵机驱动板的使用方法

简介 PCA9685驱动板支持控制16路舵机,采用i2c通信协议。 VCC:PCA9685芯片的电源 V+:舵机的电源 舵机数量较少时电源可以通过V+引脚输入,较多时通过绿色电源输入端输入 Arduino驱动示例 #include <Wire.h> #include <Adafruit_PWMServoDriverh> #define SERVO_45 187 Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); void setup() { pwm.begin(); pwm.setPWMFreq(50); pwm.setPWM(0,0,SERVO_45); } 本示例使用了Adafruit PWM Servo Driver Library。 该程序可以使连接在PCA9685舵机驱动板上0号位的舵机转动45°。 setPWMFreq()设置了IIC通信的更新频率(50HZ对应脉冲周期20ms),setPWM()`的三个参数依次为舵机编号、脉冲起始时间、脉冲结束时间,其中脉冲时间要以寄存器的值来表示(187对应1.0ms)。 舵机控制逻辑 上述示例使用舵机型号为SG90,运动范围0°-180°,舵机的控制周期信号为20ms,工作脉冲在0.5ms-2.5ms之间,舵机的转动角度由PCA9685的12位寄存器中的值来决定,而寄存器的值由脉冲时间决定。故可以得到脉冲时间-寄存器值-转动角度的关系如下: '0.5ms-0°':0.5*204=102 '1.0ms-45°':1*204=204 '1.5ms-90°':1.5*204=306 '2.0ms-135°':2*204=408 '2.5ms-180°':2.5*204=510 然而在实际使用时会有误差出现,故除了0°与180°的情况,要将寄存器值乘以0.915的系数。 '0.5ms-0°':0.5*204=102 '1.0ms-45°':1*204=204*0.915=187 '1.5ms-90°':1.5*204=306*0.915=280 '2.0ms-135°':2*204=408*0.915=373 '2.5ms-180°':2.5*204

September 28, 2023 · 1 min

ESP8266-NodeMCU食用指南

基础知识 WIFI模式 接入点模式 esp8266作为接入点,其他设备通过wifi与esp8266连接。 示例: /* 此程序用于演示如何将NodeMCU以接入点模式工作。通过此程序,您可以使用 电脑或者手机连接NodeMCU所建立WiFi网络。 */ #include <ESP8266WiFi.h> // 本程序使用ESP8266WiFi库 const char* ssid = "wifi_name"; const char* password = "12345678"; // 如果建立的WiFi不要密码,则在双引号内不要填入任何信息 void setup() { Serial.begin(9600); // 启动串口通讯 WiFi.softAP(ssid, password); // 此语句是重点。WiFi.softAP用于启动NodeMCU的AP模式。 Serial.print("Access Point: "); // 通过串口监视器输出信息 Serial.println(ssid); // 告知用户NodeMCU所建立的WiFi名 Serial.print("IP address: "); // 以及NodeMCU的IP地址 Serial.println(WiFi.softAPIP()); // 通过调用WiFi.softAPIP()可以得到NodeMCU的IP地址 } void loop() { } 无线终端模式 esp8266与路由器连接。 /* 此程序用于演示如何将NodeMcu以接入点模式工作 */ #include <ESP8266WiFi.h> const char* ssid = "wifi_name"; const char* password = "pswd"; void setup(){ Serial....

September 20, 2023 · 3 min