image frame

All those... moments... will be lost in time, like tears... in... rain.

【项目】Chat with GeoGebra

做了一个将llm接入GeoGebra的绘图辅助工具,挂载在vercel上,受于限制,每次回复的截断时间为30s。

大小受到限制,需要点开。

传送门

视频演示: (electron 版本)

效果如下



【项目】免费简历生成器

最近找工作写简历,没想到原本免费的几个简历制作软件都要收费才能导出pdf了,于是使用v0编写了这一款完全免费,无需注册,无服务器的静态网页用来排版生成简历并导出pdf。

目前还是 demo,后续会加上模板功能。

传送门

一个通过猜测api绕过支付的案例

背景

案例已去除所有敏感信息.

前段时间考研时接到的一个需求, 客户是抓取自己的网站, 可能是用来做测试?

该网站是售卖现在比较火的龙傲天短剧, 通常是那种每集2分钟, 动辄100+集的龙傲天短剧。通常采用前xx集免费,等消费者看上头了,后面剧集便需要通过支付购买方可解锁观看,由此实现变现。

支付这个功能实际上是对安全的要求十分高的,通常使用大厂的接口会靠谱的多。然而,支付接口也只是支付这一大块功能中的一个组成部分。即便使用了靠谱的支付接口,其他方面的逻辑漏洞也又可能导致

本次的案例就是支付接口之外的漏洞。该网站在消费者支付后,会在后端验证该消费者的消费信息后,给该主机后台发送所购买的视频的链接。在前端并不能看到这个链接,也看不出什么异常。

分析

在多次的抓包分析后,发现了这样的问题:对付费视频的api做请求时,请求中并没有任何用于检验身份的token。这就意味着,未付费的消费者如果碰巧猜测到了付费视频的api,那么便可以不用付费即可获得视频数据。

本次案例中的视频api有这样的结构:xxxx.com/video_id/num_id/base.m3u8. 其中当集数增加1后,base以36进制也增加1。由此,我们可以通过前一段免费的视频来猜测后面收费视频的api。

问题的关键在base+1的实现。该base是一个字符串,每个位上的字符取值集合为[0-9a-z],按照顺序在数值上主次+1,也就是4+1=5,d+1=e,当z+1后会变回0,并向高位进位。数学上与36进制数字同构。

实现

关键实现一个36进制数字的类:

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
class ABNum:
def __init__(self, value: str | int, pad=-1) -> None:
self.pad = pad
if isinstance(value, int):
self.value = value
return
value_list = [self.map_to_int(v) for v in reversed(value)]
self.pad = len(value_list)
self.value = 0
for index, v in enumerate(value_list):
self.value += v * 36 ** index

def plusOne(self):
self.value += 1

def __add__(self, other):
if isinstance(other, int):
value = self.value + other
return ABNum(value, self.pad)

def __str__(self):
value = self.value
value_list = []
while True:
value, div = divmod(value, 36)
value_list.append(div)
if value < 36:
value_list.append(value)
break
pad_num = self.pad - len(value_list)
value_list.reverse()
if pad_num > 0:
value_list = [0] * pad_num + value_list
return ''.join([self.map_to_str(v) for v in value_list])

@staticmethod
def map_to_int(v: str) -> int:
assert len(v) == 1
if v.isdecimal():
return int(v)
return ord(v) - 87

@staticmethod
def map_to_str(v: int) -> str:
if v >= 0 and v <= 9:
return str(v)
return chr(87+v)

该类并没有完全实现作为一个数字该实现的所有方法,这里仅满足使用即可。算法使用常规的短除法,按照数字定义,从10进制与36进制之间转换。类的内部使用10进制保存数值以及处理算术运算,在需要时再转换为36进制即可。

需要注意的点有:

1. 注意原始36进制数据的位数,最终求出来的36进制数要记得填充0.

2. 注意10进制与36进制转换时,需要做reverse.

总结

作为渗透测试人员、爬虫工程人员,要多留意数据之间的潜在关系。看出关系后,要能联系到可以实现的数学表达。遇到该类网站,未了解全貌后不要擅自爬取。本质上这个已经有些超过合法爬虫的范围,已经触犯法律了。可以提醒站长漏洞。

作为网站运营,这种出售链接的方式,应当使用完全随机的链接,以防止攻击者猜测出规律,绕过支付。更稳妥的方式还是验证浏览者身份信息,以及即时生成随机的视频链接。

理解pandas架构

前言

Pandas 是数据科学中常用的数据处理库;然而初学者并没有学习到Pandas的思想,仍然用数组的思维将Pandas仅仅作为数据的容器,总使用迭代的方式处理数据,这样既不优雅,又容易犯错;此外Pandas的API众多,初学者很容易迷失在茫茫的API文档中,不得要领;

数据模型

首先需要牢记的是,Pandas采用计算式;也就是说绝大多数的操作都是计算出一个结果,而并不修改原数据;如果你需要对原数据作出修改,通常需要手动重新赋值,或者加上inplace参数;

初识数据

这是一个常规数据表的结构示意图:

在Pandas中,数据由Series与DataFrame表示;其中DataFrame可以视为Series的集合;

索引

列名

特征(列)

观测(行)

选择

如果是选择某列,可以直接采用下面的方式选取:

1
col = df["col_name"]

此时得到的是一个Series对象;

若选择多列,则采用下面的方式选取:

1
cols = df[["col_name_1", "col_name_2", "col_name_3"]]

此时得到的cols是一个DataFrame对象。

若选择涉及到行,则需要使用数据框的loc/iloc接口;

loc

loc 暴露了数据框根据index对数据进行选择的接口:

1
sub_df = df.loc[]

iloc

iloc 暴露了数据框根据位置对数据进行选择的接口:

1
sub_df = df.iloc[2:8, 1:9]

筛选

筛选的过程分成两步:

  1. 根据筛选条件生成掩码;

  2. 根据掩码对数据进行筛选。

掩码

以行方向举例,每行都是一个独立的观测

用筛选条件,对所有观测做一个映射,满足筛选条件则映射到True,否则映射到False;

由此获得一个与索引长度相同的掩码数组;

根据该掩码数组,对应为True的观测被保留;对应为False的观测被舍弃。

上述为通用的筛选逻辑,其中前文介绍的loc/iloc接口均可以使用,例如:

1
df.loc[lambda s: s['shield'] == 8, :]

处理

数据处理常用的三大函数类:apply、map、reduce

这三大函数类都接受一个处理函数func、一个序列seq、以及其他的定制参数作为参数,但它们在数据处理的逻辑上会有一些差别。

apply

apply类函数会将序列整体作为一个参数传递给处理函数,该处理函数会将序列整体作为参数进行处理,并返回一个值作为结果;

从效果上来看比较类似:

1
2
3
df['col_name'].apply(func)

func(df['col_name'])

但当apply的序列对象为一个DataFrame时,apply会作用在DataFrame的每个Series上,并将每个Series的计算结果合并成一个Series。此时需要额外传递一个参数axis指明apply的方向。默认0代表行方向,1代表列方向。

1
df.apply(func, axis=1)

map

map类函数与apply类似,接受一个处理函数func,一个序列seq作为参数;不同的是,apply会将seq作为一个整体被func调用,但在map中,func会作用在seq中的每个元素上,并返回对应的计算值。最终所有计算值由map收集,并拼凑成一个与原seq形状相同的数据结构返回。

通常map只作用在Series上:

1
2
3
df['col_name'].map(func)

[func(i) for i in df['col_name']]

如果需要对DataFrame整体进行map操作,api为applymap

reduce

reduce类函数同样接受一个处理函数func,一个序列seq作为参数;

reduce函数会取seq中前两个元素s1, s2作为参数去调用func:func(s1, s2),并将结果继续与后续元素做func运算,直到消耗完seq中的所有元素,并将最后的规约值作为结果返回;

1
2
3
df['col_name'].reduce(func)

func(func(func(func(s1, s2), s3), s4)...)

合并

数据合并通常有两类:具有相同特征的两个数据框按行合并成具有更多观测的数据框、具有相同观测的两个数据框按列合并成具有更多特征的数据框。

通常前者更简单,使用concat:

1
df = pd.concat(df_1, df_2)

往往采用外连结的方式保证所有特征都被保留。

后者通常会复杂一些,往往使用merge:

1
df = pd.merge(df_1, df_2, left_on="left_col", right_on="right_col", how="left")

这里参考数据库的左右连接与内外连接,两者逻辑上完全一致。

重铸

重铸这个词来自R中的数据处理。

用来修改数据的结构。

melt

stack

unstack

窗口 window

聚合 groupby

groupby 同样是数据处理中最重要的函数之一。

groupby

groupby 将数据按照某一标准分类,以便让方便后续的apply操作;

groupby通常传入列名即可,pandas会将改列值相同的观测分成一类;

1
df.groupby('col_name')

groupby返回的是一个惰性对象,也就是说分类并不会立刻开始,而是会在后续运算时一并运算。

常见的会在其后加上

Pandas API 一览

Series 是Pandas 中的最小单位

强化学习笔记

教材:蘑菇书

绪论

  • 强化学习与监督学习的不同

    • 强化学习输入的样本是序列数据,而不像监督学习里面样本都是独立的。
    • 强化学习需要不断的试错探索。探索和利用是强化学习里核心的问题。需要在探索和利用之间找到一个权衡。
    • 强化学习里没有强监督者,只有奖励信号,并且奖励信号是延迟的。
    • 强化学习可以达到超人例子,而监督学习只能接近人。
  • 一些术语和概念

    • 通过预演(rollout)获取一系列观测

    • 每个观测称之为一个轨迹(trajectory)

    • 轨迹是当前帧状态以及它的策略动作的序列:$(s_0, a_0, s_1, a_1,…)$

    • 每个观测结束,我们可以通过观测序列以及最终奖励(eventual reward)来训练智能体

    • 一场游戏成为一个回合(episode)或者实验(trial)

      强化学习 -> 深度强化学习
      
      将特征工程并入深度学习中
      

序列决策

智能体和环境

  • 概念
    • 强化学习研究的问题是智能体与环境交互的问题

奖励

  • 概念
    • 奖励是由环境给的一种标量的反馈信号,可以显示智能体在某一步的某个策略的表现。
    • 强化学习的目的是最大化期望的累积奖励

序列决策

奖励分为近期奖励和远期奖励,两者的权衡是强化学习的重要课题之一

历史是观测、动作、奖励的序列:
$$H_t=o_1, a_1, r_1,…,o_t,a_t,r_t$$

智能体的当前动作依赖于它之前得到的历史,整个游戏的(智能体)状态可以看为关于历史的函数:
$$s_t=f(H_t)$$

此外,环境有自己的函数:
$$s^e_t=f^e(H_t)$$

智能体自身的函数:
$$s^a_t=f^a(H_t)$$

观测与状态:

状态是对世界的完整描述
观测是对状态的部分描述(通常是智能体能获取的信息)

完全可观测和部分可观测

完全可观测指智能体状态和环境状态等价,此时通常建模为马尔可夫决策过程,此时$o_t=s^e_t=s^a_t$
部分可观测指智能体无法获取环境运作的所有状态,此时通常建模为部分可观测马尔可夫决策过程

动作空间

动作空间指给定环境下,有效动作的集合

离散动作空间
连续动作空间

智能体的组成成分和类型

策略,智能体用策略选择下一步的动作
价值函数,用价值函数评估智能体当前的状态
模型,表示智能体对环境状态的理解

策略

策略是一个函数,将输入状态映射到动作

随机性策略,使用$\pi$函数,$\pi(a|s)=p(a_t=a|s_t=s)$,根据概率分布选择动作。
确定性策略,$a = argmax\pi(a|s)$,容易被对手预测

价值函数

价值函数的值是对未来奖励的预测,用于评估状态的好坏。

折扣因子是将收益从时间向收益的转换

价值函数定义:
$$V_{\pi}(s)=E_{\pi}[G_t|s_t=s]=E[\sum_{k=0}^{\INF}\gamma^kr_{t+k+1}|s_t=s],对于所有的s\inS$$

另一种价值函数Q函数,包含两个变量,状态和动作,定义为
$$Q_{\pi}(s)=E_{\pi}[G_t|s_t=s, a_t=a]=E[\sum_{k=0}^{\INF}\gamma^kr_{t+k+1}|s_t=s, a_t=a]$$

Q函数是需要学习的函数,可以获取某个状态需要采取的最优动作。

模型

模型由转移概率和奖励函数组成。

转移概率:
$$p^a_{ss’}=p(s_{t+1}=s’|s_t=s,a_t=a)$$

奖励函数值当前状态采取某动作,可以获得的奖励:
$$R(s, a)=E[r_{t+1}|s_t=s, a_t=a]$$

策略 + 价值函数 + 模型 => 马尔可夫决策过程

智能体分类

学习过程

基于策略的学习,基于策略的智能体,有策略梯度算法
基于价值的学习,基于价值的智能体,有Q学习,Sarsa算法,适用离散环境,连续环境下效果差
演员-评论员智能体,类似上述两者的集成

模型

有模型,通过学习状态的转移来采取策略,类似多了特征工程,可以一定程度减缓数据匮乏的问题
免模型,通过学习价值函数和策略函数做决策,泛化性强,消除了特征工程与真实环境之间的信息损失

学习与规划

学习与规划是序列决策的两个基本问题。

探索和利用

探索和利用是强化学习的两个核心问题。

探索-利用窘境

单步强化学习

知道每个动作的奖励
执行奖励最大的动作

单步强化学习对应K-臂赌博机模型。

仅探索法 (类似创业
仅利用法 (类似上班

马尔可夫决策过程

马尔可夫过程

掠过

马尔可夫性质

齐次马尔可夫,简化版

马尔可夫链

马尔可夫奖励过程

比马尔可夫链多了一个奖励函数

回报与价值函数

注意这里,回报、价值、奖励是不同的概念

奖励 r: 奖励是当前时刻触发的收益
回报 g: 回报是当前状态往后,直到最终时刻所获的所有收益
价值 v: 价值函数是回报的期望

贝尔曼方程

贝尔曼方程是价值函数的另一种形式

价值函数 = 即时奖励 + 未来所有奖励

可以通过价值函数的贝尔曼形式得到解析解,问题在于通常来说P是不知道的,需要学习

解析解的问题在于复杂度很高,对于高状态的矩阵算起来非常困难

迭代算法

蒙特卡洛

动态规划

时序差分学习

马尔可夫决策过程

决策过程中多一个智能体做决策的动作

价值函数 V

Q 函数

Q 函数是基于 V 函数的动作层面的空间划分

Q 是某个动作的价值,指站在此状态,选择某个动作的价值

V 是所有动作总空间的期望价值,指站在此状态,对所有动作的价值的期望

贝尔曼期望方程

先把价值函数写成Q函数的分解,再对Q函数作贝尔曼方程分解,得到一个贝尔曼期望方程的一种形式

先把价值函数写成即时奖励和后续奖励的分解,再将后续奖励中的价值函数作贝尔曼方程分解,得到贝尔曼期望方程的另一种形式

备份图

备份图描述了状态价值函数的计算分解

策略评估

前提:已经马尔可夫决策过程、策略

此时不断迭代,所有的价值函数最终都会收敛

预测与控制

预测:已知马尔可夫决策过程,策略,计算每个状态的价值

控制:已知马尔可夫决策过程,虚招最佳策略,以及最佳价值函数

动态规划

dp 解决同时有最优子结构和重叠子问题性质的问题。马尔可夫决策过程就具有这种性质,可以用动态规划去解。

马尔可夫决策过程中的策略评估

前提:已经马尔可夫决策过程、策略

将贝尔曼期望备份转换为迭代过程,直到收敛,这个过程是同步备份。

马尔可夫决策过程的控制

如何寻找最优策略,进而计算最佳价值函数

朴素方法:穷举

迭代算法:策略迭代、价值迭代

策略迭代

策略评估 + 策略改进

巨大的基础:马尔可夫过程已知。

  1. 初始化策略,和价值函数
  2. 根据当前策略,计算状态新价值函数
  3. 根据状态价值函数,推算Q 函数
  4. 最优化Q 函数,得到新策略
  5. 判断迭代是否结束,未结束,则跳转到2

基础:马尔可夫过程已知

策略 + 价值函数 –贝尔曼方程迭代–> 新价值函数
价值函数 –> Q 函数
最大化Q 函数 –> 新的策略

Q 函数可以看成一个Q 表格

这个是大致的策略迭代算法

价值迭代

最优性原理: 一个策略在状态s达到最优价值的充要条件是,任何可以从s到达的状态s’都达到了最优价值。

算法:

更新Q ,更新V 若干次
从最后的V中提取最优策略

表格型方法

使用查找表

比如蒙特卡洛、Q学习、Sarsa

有模型

使用概率函数和奖励函数来描述环境

免模型

因为很多时候环境未知,概率函数甚至奖励函数都未知。

此时免模型需要通过试探来估计概率函数等环境信息。

有模型和免模型

有模型可以直接从环境推导智能体
免模型需要不断和环境交互,迭代智能体

Q 表格

Q 表格是主要的训练对象

强化是指用下一个状态的价值来更新当前状态的价值。

免模型预测

前提:无法获取马尔可夫决策过程模型

蒙特卡洛方法

蒙特克罗使用采样,使用经验品君回报估计价值函数

优点:不需要状态转移函数和奖励函数,也不用动态规划中的自举。

缺点:只能用于有终止的马尔可夫决策过程

算法:

蒙特克罗和动态规划比较

  1. 蒙特克罗适用于环境未知,而且更新速度快
  2. 动态规划适用于有模型,但每次迭代需要更新所有状态,速度很慢

时序差分方法(TD)

免模型控制

策略迭代进行广义推广,兼容蒙特卡洛和时序差分,也就是广义策略迭代。

Sarsa 同策略时序差分控制

Q 学习 异策略时序差分控制

同策略和异策略的区别

一个例子:用Q 学习解决悬崖寻路问题

策略梯度

策略梯度算法

策略梯度实现技巧

1. 添加基线

2. 分配合适的分数

蒙特卡洛策略梯度

一个例子:用策略梯度算法解决悬崖寻路问题

近端策略优化

一个例子:用近端策略优化算法解决悬崖寻路问题

深度Q 网络

  • Copyrights © 2023-2025 Ivory
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信