PowerPoint 技巧

插入元素

插入网页

  • WebView加载项:可以在ppt应用市场中获取

    • 只支持https页面
      • 本地页面都不支持
      • 尝试自建https服务器(自签发证书)失败
    • 可以在编辑状态查看页面效果
    • 在OFFICE2010及以前不可用
  • Microsoft Web Browser控件

    • 调用IE渲染页面,因此网页对IE的兼容性很重要
    • 控件不会自动加载网页,需要通过VB通过触发事件调用其 Navigate2方法加载网页,所以只有在ppt播放页面才能 看到实际效果
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      // 页面切换事件
      // 注意不要`Private Sub`,否则事件不会被触发
      // 若想手动触发可以使用button控件的`CommandButton<X>_Click`事件
      Sub OnSlideShowPageChange()
      Dim FileName As String
      FileName = "<FILENAME>"
      // `WebBrowser1`:控件名称,唯一(单个slide内)标识控件
      // `ActivePresentation.PATH`:当前工作目录(未保存文件返回空),
      // 浏览器默认`http://`协议
      // `Navigate`方法可能会无法加载
      WebBrowser1.Navigate2(ActivePresentation.PATH + "/" + "<FILENAME>")
      End Sub

Nginx 使用、配置

配置

  • 配置文件夹为/etc/nginx

基本配置

nginx.conf

nginx.conf:Nginx主配置文件,包含“全部配置”

  • 默认include

    • modules-enabled/*.conf:已启用模块
    • mime.types:代理文件类型
    • conf.d/*.conf:服务器设置
    • sites-enabled/*:已启用站点
  • user表示启动nginx进程的用户

    • 键值默认为www-data
    • 可修改为其他用户,使nginx具有访问某些文件的权限
    • 若是在nginx启动之后修改,需修改/var/log/nginx中 log文件的属主,否则无法正常log

http服务器设置

1
2
3
4
5
6
7
8
9
10
server{
listen 8080;
server_name localhost;
location / {
root /home/xyy15926/Code;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
}
  • root:需要nginx进程可访问,否则403 Forbidden
  • autoindex:自动为文件生成目录
    • 若目录设置index index.XXX,不设置autoindex访问 目录则会403 Forbidden

https服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server{
listen 443 ssl;
server_name localhost;
ssl_certificate /home/xyy15926/Code/home_config/nginx/localhost.crt;
ssl_certificate_key /home/xyy15926/Code/home_config/nginx/localhost.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
location / {
root /home/xyy15926/Code/statics;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
}
  • ssl_certificatessl_certificate_key:SSL证书、私钥

    1
    2
    3
    4
    5
    6
    # 个人签发https证书,个人一般不会是受信任机构,所以还需要
    # 将证书添加进受信任机构(但是windows下有些有问题)
    openssl req -x509 -out localhost.crt -keyout localhost.key \
    -newkey rsa:2048 -nodes -sha256 \
    -subj '/CN=localhost' -extensions EXT -config <( \
    printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")

运行

1
2
3
 # Nginx启动、重启动
$ /etc/init.d/nginx start
$ /etc/init.d/nginx restart

Linux System Call

Kernel

内核:提供硬件抽象层、磁盘及文件系统控制、多任务等功能的系统 软件

  • 内核是操作系统的核心、基础,决定系统的性能、稳定性

    • 内核单独不是完整的操作系统
  • 内核为应用程序提供对硬件访问

    • 应用程序对硬件访受限
      • 内核决定程序何时、对何种硬件操作多长时间
    • 内核提供硬件抽象的方法隐藏对硬件操作的复杂
      • 为应用程序和硬件提供简洁、统一的接口
      • 简化程序设计

内核功能

  • 进程管理:实现了多个进程在CPU上的抽象

    • 负责创建、销毁进程,处理它们和外部输入、输出
    • 处理进程之间通讯
      • 信号
      • 管道
      • 通讯原语
    • 调度器控制进程如何共享CPU
  • 内存管理:内存是主要资源,对其管理策略对性能影响非常重要

    • 为所有进程在有限资源上建立虚拟寻址空间
    • 内核不同部分与内存管理子系统通过函数调用交互,实现 mallocfree等功能
  • 文件管理:Linux很大程度上基于文件系统概念,几乎任何东西 都可以视为是文件

    • 在非结构化硬件上建立了结构化文件系统
    • 支持多个文件系统,即物理介质上的不同数据组织方式
  • 驱动管理

    • 除CPU、内存和极少的硬件实体外,基本设备控制操作都由 特定的、需寻址的设备相关代码(设备驱动)进行
    • 内核中必须嵌入系统中出现每个外设驱动
  • 网络管理

    • 网络必须由系统管理
      • 大部分网络操作不是特定于某个进程:进入系统的报文 是异步事件
      • 系统在进程接手报文前收集、识别、分发,在程序和 网络接口间递送数据报文,根据程序的网络活动控制 程序执行
    • 路由、地址解析也在内核中实现

System Call

系统调用:操作系统提供的实现系统功能的子程序、访问硬件资源 的唯一入口

  • 系统调用是用户空间进程访问内核、硬件设备的唯一手段

    • 用户空间进程不能直接访问内核、调用内核函数
    • 对计算机硬件资源的必须经过操作系统控制
      • 计算机系统硬件资源有限,多个进程都需要访问资源
  • 系统调用与硬件体系结构紧密相关

    • 在用户空间进程和硬件设备之间添加中间层,是二者沟通的 桥梁
    • 是设备驱动程序中定义的函数最终被调用的一种方式

syscall_routine_procedure

系统调用意义

  • 用户程序通过系统调用使用硬件,简化开发、移植性

    • 分离了用户程序和内核的开发
      • 用户程序忽略API具体实现,仅借助其开发应用
      • 内核忽略API被调用,只需关心系统调用API实现
    • 为用户空间提供了统一硬件抽象接口,用户程序可以方便在 具有相同系统调用不同平台之间迁移
  • 系统调用保证了系统稳定和安全

    • 内核可以基于权限和其他规则对需要进行的访问进行 裁决
    • 避免程序不正确的使用硬件设备、窃取其他进程资源、 危害系统安全
  • 保证进程可以正常运行在虚拟寻址空间中

    • 程序可以随意访问硬件、内核而对此没有足够了解, 则难以实现多任务、虚拟内存
    • 无法实现良好的稳定性、安全性

通知内核

  • 大部分情况下,程序通过API而不是直接调用系统调用
  • POSIX标准是Unix世界最流行的API接口规范,其中API和系统 调用之间有直接关系,但也不是一一对应

系统调用表

系统调用表sys_call_table:其中元素是系统调用服务例程的 起始地址

1
2
3
4
5
// arch/x86/entry/syscall_64.c
asmlinkage const sys_call_ptr sys_call_table[__NR_syscall_max+1] ={
[0 ... __NR_syscall_max] = &sys_ni_syscall,
#include <asm/syscalls_64.h>
};
  • ...是GCCDesignated Initializers插件功能,此插件允许 无序初始化元素值
  • sys_call_table是长为__NR_syscall_max+1的数组

  • __NR_syscall_max是给定系统架构所允许的最大系统调用 数目

    1
    2
    // include/generated/asm-offsets.h
    #define __NR_syscall_max 547
    • 此数值必然和arch/x86/entry/syscalls/syscall_64.tbl 中最大系统调用数值相同
  • sys_call_ptr是指向系统调用表指针类型

    1
    typedef void (*sys_call_ptr_t)(void);
  • sys_ni_syscall是返回错误的函数

    1
    2
    3
    asmlinkage long sys_ni_syscall(void){
    return -ENOSYS;
    }
    • 未在<asm/syscalls_64.h>定义系统调用号将会对应此 响应函数,返回ENOSYS专属错误
  • <asm/syscalls_64.h>由脚本 arch/x86/entry/syscalls/syscalltbl.sharch/x86/entry/syscalls/syscall_64.tbl中生成

    1
    2
    3
    4
    5
    6
    7
    // <asm/syscalls_64.h>
    __SYS_COMMON(0, sys_read, sys_read)
    __SYS_COMMON(0, sys_write, sys_write)

    // <arch/x86/entry/syscall_64.c>
    #define __SYSCALL_COMMON(nr, sym, compat) __SYSCALL_64(nr, sym, compat)
    #define __SYSCALL_64(nr, sym, compat) [nr] = sym

系统调用号

1
2
3
4
5
6
7
/* fs/xattr.c */
#define __NR_setxattr 5
__SYSCALL(__NR_setxattr, sys_setxattr)
#define __NR_lsetxattr 6
__SYSYCALL(__NR_lsetxattr, sys_lsetattr)
#define __NR_fsetxattr 7
__SYSYCALL(__NR_fsetxattr, sys_fsetattr)

系统调用号_NR_XXX:系统调用唯一标识

  • 系统调用号一旦分配不能有任何变更,否则编译好的程序会崩溃

    • 系统调用号定义在include/asm/unisted.h
  • 用户空间进程通过系统调用号指明需要执行的系统调用

    • 系统调用号就是系统调用在sys_call_table中的偏移
    • 根据系统调用号在sys_call_table中找到对应表项内容, 即可找到系统调用响应函数sys_NAME的入口地址
  • 所有系统调用陷入内核的方式相同,所以必须把系统调用号一并 传给内核

    • X86机器上,系统调用号通过eax寄存器传递给内核
      • 陷入内核态,用户空间进程已经把系统调用对应系统 调用号放入eax
      • 系统调用一旦运行,就可以从eax中得到数据

陷入指令

  • 系统调用通过陷入指令进入内核态

    • 然后内核根据存储在寄存器中的系统调用号在系统调用表 中找到相应例程函数的入口地址
    • 根据入口地址调用例程函数
  • 陷入指令是特殊指令,且依赖于机器架构,在X86机器中指令为 int 0x80

    • 不应直接使用陷入指令
    • 应实现系统调用库函数,以系统调用号为参数,执行陷入 指令陷入内核态,并执行系统调用例程函数,即 __syscall[N]系列宏

__syscall[N]

_syscall[N]:方便用户程序访问系统调用的一系列宏

1
2
3
4
5
6
// linux/include/asm/unistd.h
__syscall0(type, name)
__syscall1(type, name, type1, args1)
__syscall2(type, name, type1, arg1, type2, arg2)
// 0-6共7个宏
__syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5, type6, arg6)
1
2
3
4
5
6
7
8
9
10
#define _syscall2(type, name, type1, arg1, type2, arg2) \
type name(type1, arg1, type2, arg2) \
{ \
long _res; \
__asm__ volatile ("int $0x80" \
: "=a" (_res) \
: "0" (__NR##name), "b" ((long)(arg1)), "c" ((long)(arg2))); \
// some code
__syscall_return(type, __res)
}
  • 7个宏分别可以适用于参数个数为0-6的系统调用 (超过6个参数的系统调用罕见)

  • __syscall[N]宏根据系统调用名创建name同名函数,通过 该函数即可访问系统调用

  • 大部分情况下用户程序都是通过库函数访问系统调用,调用 __syscall[N]系列宏通常由库函数完成
  • Linux2.6.19内核之后弃用

syscall

syscall:通过系统调用号相应参数访问系统调用

1
2
3
4
5
6
7
8
9
10
11
int syscall(int number, ...);

#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>

int main(int argc, char *argv){
pid_t tid;
// `SYS_[NAME]`是系统调用号常量
tid = syscall(SYS_getpid);
}

System Call Service Routine

系统调用服务例程/响应函数:系统调用的实际处理逻辑

  • 一般以sys_开头,后跟系统调用名

    • fork()的响应函数是sys_fork()
    • exit()的响应函数是sys_exit()
  • 系统调用可以看作是系统调用服务例程在内核中注册的 名,内核通过系统调用名寻找对应服务例程

    1
    2
    3
    4
    // arch/x86/entry/syscalls/syscall_64.tbl
    0 common read sys_read
    1 common write sys_write
    2 common open sys_open

参数传递

参数传递

  • 除系统调用号外参数输入同样存放在寄存器中
    • X86机器上,ebxecxedxesiedi按照顺序 存放前5个参数
    • 需要6个以上参数情况不多,此时应该用单独的寄存器存放 指向所有参数在用户空间地址的指针

参数验证

  • 系统调用需要检查参数是否合法有效、正确

    • 文件IO相关系统调用需要检查文件描述符是否有效
    • 进程相关函数需要检查PID是否有效
  • 用户指针检查,内核必须保证

    • 指针指向的内存区域属于用户空间:不能哄骗内核读取 内核空间数据
    • 指针指向的内存区域在进程地址空间:不能哄骗内核读取 其他进程数据
    • 进程不能绕过内存访问限制:内存读、写应被正确标记

访问系统调用

系统调用初始化

  • 系统调用初始化就是对陷入指令的初始化,在X86机器上就是 初始化INT 0x80指令

  • 系统启动时

    • 汇编子程序setup_idt()准备256项的idt表
    • start_kernel()trap_init()调用的C宏定义 set_system_gate(0x80, &system_call)设置0x80号 软中断服务程序为system_call
    • system_call就是所有系统调用总入口

系统调用上下文

  • 内核在执行系统调用时处于进程上下文

    • current指针指向当前任务,即引发系统调用的进程
  • 在进程上下文中,内可以休眠、被抢占

    • 能够休眠:系统调用可以使用内核提供的绝大部分功能, 方便内核编程
    • 能被抢占:新进程可以使用相同的系统调用
      • 保证系统调用可重入
  • 系统调用返回时,控制权依然在system_call()中,其会负责 切换到用户空间并让用户进程继续执行

系统调用返回值

errno错误码

  • 系统调用将错误码放入名为errno的全局变量中

    • 为防止和正常返回值混淆,系统调用不直接返回错误码
    • errno值只在函数发生错误时设置,若函数不发生错误, errno值无定义,并不置为0
      • 0值通常表示成功
      • 负值表示系统调用失败
        • 错误值对应错误消息定义在error.h
        • 可以通过perror()库函数翻译误码
    • 处理errno前最好将其存入其他变量中,因为在错误处理 过程中errno值可能会被改变
  • 系统调用具有明确的操作

ret_from_sys_call

  • ret_from_sys_call为入口的汇编程序段在Linux进程管理中 起到重要作用

    • 系统调用结束前、大部分中断服务返回前,都会跳转至此处 入口地址
    • 还处理中断嵌套、CPU调度、信号等
  • 给用户空间进程的返回值同样通过寄存器传递

    • X86机器上,存放在eax寄存器中

Linux系统调用、派生函数

进程管理

进程控制

  • fork:创建新进程
  • clone:按照指定条件创建子进程
  • execve:运行可执行文件
  • exit:终止进程
  • _exit:立即终止当前进程
  • getdtablesize:进程能打开的最大文件数
  • getpgid:获取指定进程组标识号
  • setpgid:设置指定进程组标识号
  • getpgrp:获取当前进程组标识号
  • setpgrp:设置当前进程组标识号
  • getpid:获取进程标识号
  • getppid:获取父进程标识号
  • getpriority:获取调度优先级
  • setpriority:设置调度优先级
  • modify_ldt:读写进程本地描述符
  • nanosleep:使指定进程睡眠
  • nice:改变分时进程的优先级
  • pause:挂起进程,等待信号
  • personality:设置进程运行域
  • prctl:对进程进行特定操作
  • ptrace:进程跟踪
  • sched_get_priority_max:取得静态优先级上限
  • sched_get_priority_min:取得静态优先级下限
  • sched_getparam:取得进程调度参数
  • sched_getscheduler:取得指定进程的调度策略
  • sched_rr_get_interval:取得按RR算法实调度的实时进程 时间片
  • sched_setparam:设置进程调度参数
  • sched_setscheduler:设置进程调度策略和参数
  • sched_yield:进程主动出让处理器,并添加到调度队列队尾
  • vfork:创建执行新程序的子进程
  • wait/wait3:等待子进程终止
  • watipid/wait4:等待指定子进程终止
  • capget:获取进程权限
  • capset:设置进程权限
  • getsid:获取会晤标识号
  • setsid:设置会晤标识号

进程间通信

  • ipc:进程间通信控制总控制调用

信号

  • sigaction:设置对指定信号的处理方法
  • sigprocmask:根据参数对信号集中的号执行阻塞、解除 阻塞等操作
  • sigpending:为指定被阻塞信号设置队列
  • sigsuspend:挂起进程等待特定信号
  • signal
  • kill:向进程、进程组发信号
  • sigvec:为兼容BSD设置的信号处理函数,作用类似 sigaction
  • ssetmask:ANSI C的信号处理函数,作用类似sigaction

消息

  • msgctl:消息控制操作
  • msgget:获取消息队列
  • msgsnd:发消息
  • msgrcv:取消息

管道

  • pipe:创建管道

信号量

  • semctl:信号量控制
  • semget:获取一组信号量
  • semop:信号量操作

内存管理

内存管理

  • brk/sbrk:改变数据段空间分配
  • mlock:内存页面加锁
  • munlock:内存页面解锁
  • mlockall:进程所有内存页面加锁
  • munlockall:进程所有内存页面解锁
  • mmap:映射虚拟内存页
  • munmap:去除内存映射页
  • mremap:重新映射虚拟内存地址
  • msync:将映射内存中数据写回磁盘
  • mprotect:设置内存映像保护
  • getpagesize:获取页面大小
  • sync:将内存缓冲区数据写回磁盘
  • cacheflush:将指定缓冲区中内容写回磁盘

共享内存

  • shmctl:控制共享内存
  • shmget:获取共享内存
  • shmat:连接共享内存
  • shmdt:卸载共享内存

文件管理

文件读写

  • tcntl:文件控制
  • open:打开文件
  • creat:创建新文件
  • close :关闭文件描述字
  • read:读文件
  • write:写文件
  • readv:从文件读入数据到缓存区
  • writev:将缓冲区数据写入文件
  • pread:随机读文件
  • pwrite:随机写文件
  • lseek:移动文件指针
  • _llseek:64位地址空间中移动文件指针
  • dup:复制已打开的文件描述字
  • dup2:按指定条件复制文件描述字
  • flock:文件加/解锁
  • poll:IO多路切换
  • truncat/ftruncate:截断文件
  • vumask:设置文件权限掩码
  • fsync:将内存中文件数据写入磁盘

文件系统操作

  • access:确定文件可存取性
  • chdir/fchdir:改变当前工作目录
  • chmod/fchmod:改变文件模式
  • chown/fchown/lchown:改变文件属主、用户组
  • chroot:改变根目录
  • stat/lstat/fstat:获取文件状态信息
  • statfs/fstatfs:获取文件系统信息
  • ustat:读取文件系统信息
  • mount:安装文件系统
  • umount:卸载文件系统
  • readdir:读取目录项
  • getdents:读取目录项
  • mkdir:创建目录
  • mknod:创建索引节点
  • rmdir:删除目录
  • rename:文件改名
  • link:创建链接
  • symlink:创建符号链接
  • unlink:删除链接
  • readlink:读取符合链接值
  • utime/utimes:改变文件的访问修改时间
  • quotactl:控制磁盘配额

驱动管理

系统控制

  • ioctl:IO总控制函数
  • _sysctl:读写系统参数
  • acct:启用或禁用进程记账
  • getrlimit:获取系统资源上限
  • setrlimit:设置系统资源上限
  • getrusage:获取系统资源使用情况
  • uselib:选择要使用的二进制库
  • ioperm:设置端口IO权限
  • iopl:改变进程IO权限级别
  • outb:低级端口操作
  • reboot:重启
  • swapon:开启交换文件和设备
  • swapoff:关闭交换文件和设备
  • bdflush:控制bdflush守护进程
  • sysfs:获取核心支持的文件系统类型
  • sysinfo:获取系统信息
  • adjtimex:调整系统时钟
  • getitimer:获取计时器值
  • setitimer:设置计时器值
  • gettimeofday:获取时间、时区
  • settimeofday:设置时间、时区
  • stime:设置系统日期和时间
  • time:获取系统时间
  • times:获取进程运行时间
  • uname:获取当前unix系统名称、版本、主机信息
  • vhangup:挂起当前终端
  • nfsservctl:控制NFS守护进程
  • vm86:进入模拟8086模式
  • create_module:创建可载入模块项
  • delete_module:删除可载入模块项
  • init_module:初始化模块
  • query_module:查询模型信息

网络管理

网络管理

  • getdomainname:获取域名
  • setdomainname:设置域名
  • gethostid:获取主机标识号
  • sethostid:设置主机标识号
  • gethostname:获取主机名称
  • sethostname:设置主机名称

Socket控制

  • socketcall:socket系统调用
  • socket:建立socket
  • bind:绑定socket到端口
  • connect:连接远程主机
  • accept:响应socket连接请求
  • send/sendmsg:通过socket发送信息
  • sendto:发送UDP信息
  • recv/recvmsg:通过socket接收信息
  • recvfrom:接收UDP信息
  • listen:监听socket端口
  • select:对多路同步IO进行轮询
  • shutdown:关闭socket上连接
  • getsockname:获取本地socket名称
  • getpeername:获取通信对方socket名称
  • getsockopt:获取端口设置
  • setsockopt:设置端口参数
  • sendfile:在文件端口间传输数据
  • socketpair:创建一对已连接的无名socket

用户管理

  • getuid:获取用户标识号
  • setuid:设置用户标识号
  • getgid:获取组标识号
  • setgid:设置组标识号
  • getegid:获取有效组标识号
  • setegid:设置有效组标识号
  • geteuid:获取有效用户标识号
  • seteuid:设置有效用户标识号
  • setregid:分别设置真实、有效的组标识号
  • setreuid:分别设置真实、有效的用户标识号
  • getresgid:分别获取真实、有效、保存过的组标识号
  • setresgid:分别设置真实、有效、保存过的组标识号
  • getresuid:分别获取真实、有效、保存过的用户标识号
  • setresuid:分别设置真实、有效、保存过的用户标识号
  • setfsgid:设置文件系统检查时使用的组标识号
  • setfsuid:设置文件系统检查时使用的用户标识号
  • getgroups:获取候补组标志清单
  • setgroups:设置候补组标志清单

通知内核

  • 一般系统调用都是通过软件中断向内核发请求,实现内核提供的 某些服务

    • 128号异常处理程序就是系统调用处理程序system_call()
  • 用户空间进程不能直接执行内核代码,需要通过中断通知内核 需要执行系统调用,希望系统切换到内核态,让内核可以代表 应用程序执行系统调用

  • 通知内核机制是靠软件中断实现,X86机器上软中断由int产生

    • 用户程序为系统调用设置参数,其中一个参数是系统调用 编号
    • 程序执行“系统调用”指令,该指令会导致异常
    • 保存程序状态
    • 处理器切换到内核态并跳转到新地址,并开始执行异常处理 程序,即系统调用处理程序
    • 将控制权返还给用户程序
  • arch/i386/kernel/head.s

  • init/main.c
  • arhc/i386/kernel/traps.c
  • include/asm/system.h

参数传递

  • _syscalN()用于系统调用的格式转换和参数传递

    • 参数数量为N的系统调用由_syscallN()负责
    • N取值为0-5之间的整数
  • 启动INT 0x80后,规定返回值送eax寄存器

    • 定义于include/asm/unistd.h,用于系统调用的格式转换 和参数传递

Linux Interrupt

中断

  • 软中断:

  • 硬中断:外围设备完成用户请求后,会向CPU发出中断信号

    • CPU会暂停执行下条将要执行的指令,转而执行中断信号 对应处理程序,并将进程转入内核态

Interrupt

  • 中断属性

    • 中断号:标识不同中断
    • 中断处理程序:不同中断有不同处理程序
  • interrupt vector table:内核中维护,存储所有中断处理 程序地址

    • 中断号就是相应中断在中断向量表中偏移量

Excel技巧

EXCEL密码破解

VBAProject密码

  • vbaProject.bin中密码对应字段

    • DPB:当前已存在密码,后跟加密密码值
  • xls:97-03格式表格是RAR格式压缩文件

    • 解压之后再次压缩一般无法正常打开,应该是有特殊的压缩 规则
    • 可以直接用编辑器打开、修改整个文件,但要注意以二进制 格式打开
  • xlsm:07之后带宏表格是ZIP压缩文件

    • 解压之后再次zip打包也无法正常打开,但是zip格式可以 直接替换其中文件,所以可以直接修改单个文件
    • vim对zip格式文件处理类型文件夹,所以使用vim二进制打开 可以修改其中单个文件

修改二进制文件

  • 处理方法:将密码对应字段“无效”

    • 需要保持文件前后大小不改变
    • 密码字段无效化
  • “删除”密码字段

    • DPB字段替换为等长其他名称
      • 密码无法被正常识别
      • 文件大小没有改变,仅字符被替换
    • 再次打开文件,启用宏会报错
      • 不断点击确认之后即可查看vba工程
      • 为vba工程设置新密码,覆盖替换后错误字段,保存 可得已知密码文件
        • 右键VBAProject
        • 工程属性
        • 保护
        • 查看工程属性密码:修改为新密码即可
  • 替换密码字段

    • DPB后密码值替换为其他值
      • 为保证文件大小不变
        • 若新密码较短需要用0填充不足部分
        • 较原始密码长则无需额外操作
    • 打开文件则可以使用已知密码查看vba工程
1
2
3
4
5
6
7
8
9
10
11
 # 二进制打开文件,否则vim会做某些处理,损坏文件
$ vim -b <file>

# 调用xxd转换字符格式为16进制
# 可看到16进制、字符对应格式
# 仅修改左侧16进制有效,修改右侧字符表示不影响文件
# 内容
$ !%xxd

# 16进制转换回原始表示
$ !%xxd -r

VBA脚本穷举密码

  • xls格式:打开VBA编辑器执行代码

    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
    '移除VBA编码保护
    Sub MoveProtect()
    Dim FileName As String
    FileName = Application.GetOpenFilename("Excel文件(*.xls & *.xla),*.xls;*.xla", , "VBA破解")
    If FileName = CStr(False) Then
    Exit Sub
    Else
    VBAPassword FileName, False
    End If
    End Sub

    '设置VBA编码保护
    Sub SetProtect()
    Dim FileName As String
    FileName = Application.GetOpenFilename("Excel文件(*.xls & *.xla),*.xls;*.xla", , "VBA破解")
    If FileName = CStr(False) Then
    Exit Sub
    Else
    VBAPassword FileName, True
    End If
    End Sub

    Private Function VBAPassword(FileName As String, Optional Protect As Boolean = False)
    If Dir(FileName) = "" Then
    Exit Function
    Else
    FileCopy FileName, FileName & ".bak"
    End If

    Dim GetData As String * 5
    Open FileName For Binary As #1
    Dim CMGs As Long
    Dim DPBo As Long
    For i = 1 To LOF(1)
    Get #1, i, GetData
    If GetData = "CMG=""" Then CMGs = i
    If GetData = "[Host" Then DPBo = i - 2: Exit For
    Next
    If CMGs = 0 Then
    MsgBox "请先对VBA编码设置一个保护密码...", 32, "提示"
    Exit Function
    End If
    If Protect = False Then
    Dim St As String * 2
    Dim s20 As String * 1
    '取得一个0D0A十六进制字串
    Get #1, CMGs - 2, St
    '取得一个20十六制字串
    Get #1, DPBo + 16, s20
    '替换加密部份机码
    For i = CMGs To DPBo Step 2
    Put #1, i, St
    Next
    '加入不配对符号
    If (DPBo - CMGs) Mod 2 <> 0 Then
    Put #1, DPBo + 1, s20
    End If
    MsgBox "文件解密成功......", 32, "提示"
    Else
    Dim MMs As String * 5
    MMs = "DPB="""
    Put #1, CMGs, MMs
    MsgBox "对文件特殊加密成功......", 32, "提示"
    End If
    Close #1
    End Function

Sheet保护密码

VBA脚本

  • xlsx:打开VBA编辑器执行代码

    1
    2
    3
    4
    5
    6
    7
    Sub pj()
    Dim sht As Worksheet
    For Each sht In Worksheets
    sht.Protect AllowFiltering:=True
    sht.Unprotect
    Next
    End Sub
  • xls:打开VBA编辑器执行代码

    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
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    Public Sub AllInternalPasswords()
    ' Breaks worksheet and workbook structure passwords. Bob McCormick
    ' probably originator of base code algorithm modified for coverage
    ' of workbook structure / windows passwords and for multiple passwords
    '
    ' Norman Harker and JE McGimpsey 27-Dec-2002 (Version 1.1)
    ' Modified 2003-Apr-04 by JEM: All msgs to constants, and
    ' eliminate one Exit Sub (Version 1.1.1)
    ' Reveals hashed passwords NOT original passwords
    Const DBLSPACE As String = vbNewLine & vbNewLine
    Const AUTHORS As String = DBLSPACE & vbNewLine & _
    "Adapted from Bob McCormick base code by" & _
    "Norman Harker and JE McGimpsey"
    Const HEADER As String = "AllInternalPasswords User Message"
    Const VERSION As String = DBLSPACE & "Version 1.1.1 2003-Apr-04"
    Const REPBACK As String = DBLSPACE & "Please report failure " & _
    "to the microsoft.public.excel.programming newsgroup."
    Const ALLCLEAR As String = DBLSPACE & "The workbook should " & _
    "now be free of all password protection, so make sure you:" & _
    DBLSPACE & "SAVE IT NOW!" & DBLSPACE & "and also" & _
    DBLSPACE & "BACKUP!, BACKUP!!, BACKUP!!!" & _
    DBLSPACE & "Also, remember that the password was " & _
    "put there for a reason. Don't stuff up crucial formulas " & _
    "or data." & DBLSPACE & "Access and use of some data " & _
    "may be an offense. If in doubt, don't."
    Const MSGNOPWORDS1 As String = "There were no passwords on " & _
    "sheets, or workbook structure or windows." & AUTHORS & VERSION
    Const MSGNOPWORDS2 As String = "There was no protection to " & _
    "workbook structure or windows." & DBLSPACE & _
    "Proceeding to unprotect sheets." & AUTHORS & VERSION
    Const MSGTAKETIME As String = "After pressing OK button this " & _
    "will take some time." & DBLSPACE & "Amount of time " & _
    "depends on how many different passwords, the " & _
    "passwords, and your computer's specification." & DBLSPACE & _
    "Just be patient! Make me a coffee!" & AUTHORS & VERSION
    Const MSGPWORDFOUND1 As String = "You had a Worksheet " & _
    "Structure or Windows Password set." & DBLSPACE & _
    "The password found was: " & DBLSPACE & "$$" & DBLSPACE & _
    "Note it down for potential future use in other workbooks by " & _
    "the same person who set this password." & DBLSPACE & _
    "Now to check and clear other passwords." & AUTHORS & VERSION
    Const MSGPWORDFOUND2 As String = "You had a Worksheet " & _
    "password set." & DBLSPACE & "The password found was: " & _
    DBLSPACE & "$$" & DBLSPACE & "Note it down for potential " & _
    "future use in other workbooks by same person who " & _
    "set this password." & DBLSPACE & "Now to check and clear " & _
    "other passwords." & AUTHORS & VERSION
    Const MSGONLYONE As String = "Only structure / windows " & _
    "protected with the password that was just found." & _
    ALLCLEAR & AUTHORS & VERSION & REPBACK
    Dim w1 As Worksheet, w2 As Worksheet
    Dim i As Integer, j As Integer, k As Integer, l As Integer
    Dim m As Integer, n As Integer, i1 As Integer, i2 As Integer
    Dim i3 As Integer, i4 As Integer, i5 As Integer, i6 As Integer
    Dim PWord1 As String
    Dim ShTag As Boolean, WinTag As Boolean

    Application.ScreenUpdating = False
    With ActiveWorkbook
    WinTag = .ProtectStructure Or .ProtectWindows
    End With
    ShTag = False
    For Each w1 In Worksheets
    ShTag = ShTag Or w1.ProtectContents
    Next w1
    If Not ShTag And Not WinTag Then
    MsgBox MSGNOPWORDS1, vbInformation, HEADER
    Exit Sub
    End If
    MsgBox MSGTAKETIME, vbInformation, HEADER
    If Not WinTag Then
    MsgBox MSGNOPWORDS2, vbInformation, HEADER
    Else
    On Error Resume Next
    Do 'dummy do loop
    For i = 65 To 66: For j = 65 To 66: For k = 65 To 66
    For l = 65 To 66: For m = 65 To 66: For i1 = 65 To 66
    For i2 = 65 To 66: For i3 = 65 To 66: For i4 = 65 To 66
    For i5 = 65 To 66: For i6 = 65 To 66: For n = 32 To 126
    With ActiveWorkbook
    .Unprotect Chr(i) & Chr(j) & Chr(k) & _
    Chr(l) & Chr(m) & Chr(i1) & Chr(i2) & _
    Chr(i3) & Chr(i4) & Chr(i5) & Chr(i6) & Chr(n)
    If .ProtectStructure = False And _
    .ProtectWindows = False Then
    PWord1 = Chr(i) & Chr(j) & Chr(k) & Chr(l) & _
    Chr(m) & Chr(i1) & Chr(i2) & Chr(i3) & _
    Chr(i4) & Chr(i5) & Chr(i6) & Chr(n)
    MsgBox Application.Substitute(MSGPWORDFOUND1, _
    "$$", PWord1), vbInformation, HEADER
    Exit Do 'Bypass all for...nexts
    End If
    End With
    Next: Next: Next: Next: Next: Next
    Next: Next: Next: Next: Next: Next
    Loop Until True
    On Error GoTo 0
    End If
    If WinTag And Not ShTag Then
    MsgBox MSGONLYONE, vbInformation, HEADER
    Exit Sub
    End If
    On Error Resume Next
    For Each w1 In Worksheets
    'Attempt clearance with PWord1
    w1.Unprotect PWord1
    Next w1
    On Error GoTo 0
    ShTag = False
    For Each w1 In Worksheets
    'Checks for all clear ShTag triggered to 1 if not.
    ShTag = ShTag Or w1.ProtectContents
    Next w1
    If ShTag Then
    For Each w1 In Worksheets
    With w1
    If .ProtectContents Then
    On Error Resume Next
    Do 'Dummy do loop
    For i = 65 To 66: For j = 65 To 66: For k = 65 To 66
    For l = 65 To 66: For m = 65 To 66: For i1 = 65 To 66
    For i2 = 65 To 66: For i3 = 65 To 66: For i4 = 65 To 66
    For i5 = 65 To 66: For i6 = 65 To 66: For n = 32 To 126
    .Unprotect Chr(i) & Chr(j) & Chr(k) & _
    Chr(l) & Chr(m) & Chr(i1) & Chr(i2) & Chr(i3) & _
    Chr(i4) & Chr(i5) & Chr(i6) & Chr(n)
    If Not .ProtectContents Then
    PWord1 = Chr(i) & Chr(j) & Chr(k) & Chr(l) & _
    Chr(m) & Chr(i1) & Chr(i2) & Chr(i3) & _
    Chr(i4) & Chr(i5) & Chr(i6) & Chr(n)
    MsgBox Application.Substitute(MSGPWORDFOUND2, _
    "$$", PWord1), vbInformation, HEADER
    'leverage finding Pword by trying on other sheets
    For Each w2 In Worksheets
    w2.Unprotect PWord1
    Next w2
    Exit Do 'Bypass all for...nexts
    End If
    Next: Next: Next: Next: Next: Next
    Next: Next: Next: Next: Next: Next
    Loop Until True
    On Error GoTo 0
    End If
    End With
    Next w1
    End If
    MsgBox ALLCLEAR & AUTHORS & VERSION & REPBACK, vbInformation, HEADER
    End Sub

抽样方法

数据抽样

  • 抽样作用

    • 提高速度、效率,将精力放在建立模型、选择模型上
    • 帮助分析特殊性问题:有些问题涉及到破坏性试验,抽取产品的一部分做耐用性实验经济有效
    • 降低成本:合理抽样可以保证在大部分信息不丢失情况下,降低数据采集、社会调查成本
  • 从效率、成本角度看,适当、合理抽样有必要

    • 数据越多信息越丰富、数据量尽量多为好
    • 抽样可以降低求解的时空代价,但是可能会丢失部分信息,可能会使分析结果产生偏差
    • 在分析阶段,若抽样误差能够接受,完全可以抽样
  • 样本应能充分代表总体

    • 一般样本容量越大,和总体的相似程度越高,样本质量越高
    • 但大样本不等于总体:理论上再大的局部抽样也不如随机抽样有代表性

样本评价

  • 样本容量、样本质量是衡量抽样样本的两个最重要因素
    • 样本容量:抽样过程中抽取的样本数
    • 样本质量:衡量抽样样本的代表性

样本质量

样本质量:抽样样本与整体的相似性

  • $D$:数据集,包含 $r$ 个属性
  • $S$:抽样样本集
  • $J_k=J(S, D)$:Kullblack-Laible 散度,数据集 $S$、$D$ 在属性 $k$ 上偏差程度,越小偏差越小
  • $Q(S) \in [0, 1]$:抽样集 $S$ 在数据集 $D$ 中的质量,越大样本集质量越高
  • 若整体 $D$ 分布稀疏,容易得到 $S$ 在某些数据点观测值数为 0,得到 $I(S, D) \rightarrow infty$

    • 可以把该点和附近的点频率进行合并,同时调整总体频率分布
    • 过度合并会导致无法有效衡量数据集局部差异性
  • 对于连续型变量

    • 可以把变量进行适当分组:粗糙,不利于刻画数据集直接的局部差异
    • 计算数据集各个取值点的非参估计,如核估计、最近邻估计等,再在公式中用各自的非参估计代替相应频率,计算样本质量
  • 数据包含多个指标时

    • 可以用多个指标的平均样本质量衡量整体样本质量
    • 也可以根据指标重要程度,设置不同的权重

样本容量

  • 样本容量是评价样本的另一个重要维度
    • 样本量大、质量好、准确性高,但计算效率低
    • 样本质量差、准确性低、计算效率高
    • 样本质量提高不是线性的,高位样本容量上,边际效用往往较低
    • 同一样本容量的不同样本的样本质量也会有差异,即样本质量不是样本容量的单调函数,包含随机扰动

Statistical Optimal Sample Size

SOSS:统计最优样本数

  • 输入:数据集 $D$,包含 $N$ 个实例
  • 根据某种抽样方法,随机产生 $R$ 个样本容量分别为 $n_i, n_i \in [1, N]$ 的样本 $S$

    • $n_i$ 取值较小处应密度比较大,因为随着 $n_i$ 增加,样本质量趋近 1,不需要太多样本
    • 可以考虑使用指数序列产生在较大值处稀疏的序列作为 $n_i$ 序列的取值
  • 计算每个样本 $S$ 在数据集 $D$ 中的样本质量 $Q$

    • 并计算各个样本容量对应的样本质量均值 $\bar {Q_{n}}$
    • 绘制曲线 $(n, \bar {Q_{n}})$
  • 根据给定的样本质量要求,在样本容量对应样本质量的曲线上确定近似的最优样本容量

测试集、训练集

  • 测试集、训练集划分逻辑前提

    • 在样本量足够的情况下,减少部分样本量不会影响模型精度
    • 模型评价需要使用未参与建模数据验证,否则可能夸大模型效果
  • 测试集、训练集划分作用

    • 测试集直接参与建模,其包含信息体现在模型中
    • 训练集仅仅用于评价模型效果,其包含信息未被利用
    • 因此,若无评价、对比模型需求,或有其他无需划分测试集即可评价模型,则划分测试集无意义

测试集、训练集划分

Hold Out

旁置法:将样本集随机划分为训练集、测试集,只利用训练集训练 模型

  • 适合样本量较大的场合
    • 减少部分训练数据对模型精度影响小
    • 否则大量样本未参与建模,影响模型精度
  • 常用划分比例
    • 8:2
    • 7:3
  • 旁置法建立模型可直接作为最终输出模型
    • 旁置法一般只建立一个模型
    • 且使用旁置法场合,模型应该和全量数据训练模型效果差别不大

N-fold Cross Validation

N 折交叉验证:将数据分成N份,每次将其中一份作为测试样本集, 其余N-1份作为训练样本集

  • N折交叉验证可以视为旁置法、留一法的折中

    • 克服了旁置法中测试样本选取随机性的问题:每个样本都 能作为测试样本
    • 解决了留一法计算成本高的问题:重复次数少
  • 典型的“袋外验证”

    • 袋内数据(训练样本)、袋外数据(测试样本)分开
  • N折交叉验证会训练、得到N个模型,不能直接输出

    • 最终应该输出全量数据训练的模型
    • N折建立N次模型仅是为了合理的评价模型效果,以 N 个模型的评价指标(均值)作为全量模型的评价

Leave-One-Out Cross Validation

留一法:每次选择一个样本作为测试样本集,剩余 n-1 个观测值作为训练样本集,重复 n 次计算模型误差

  • 可以看作是 N 折交叉验证的特例

数据泄露

  • 特征泄露:训练过程中使用有包含有上线之后无法获取的数据

    • 时序数据中数据穿越:使用未来数据训练模型,模型将学习不应获取的未来信息
  • 记录泄露/训练数据泄露:切分数据集时训练集包含了测试集中部分数据

    • 会导致评估指标失真

样本重抽样

Bootstrap

重抽样自举:有放回的重复抽样,以模拟多组独立样本

  • 对样本量为 $n$ 的样本集 $S$
  • 做$k$次有放回的重复抽样
    • 每轮次抽取 $n$ 个样本
    • 抽取得到样本仍然放回样本集中
  • 得到 $k$ 个样本容量仍然为 $n$ 的随机样本 $S_i,(i=1,2,…,k)$

过采样

  • over-sampling:过采样,小类数据样本增加样本数量
  • synthetic minority over-sampling technique:过采样算法,构造不同于已有样本小类样本
    • 基于距离度量选择小类别下相似样本
    • 选择其中一个样本、随机选择一定数据量邻居样本
    • 对选择样本某属性增加噪声,构造新数据

SMOTE

Borderline-SMOTE

欠采样

  • under-sampling:欠采样,大类数据样本减少样本数量

特征编码

数值化:分类->数值

Ordinal Encoding

序号编码:使用一位序号编码类别

  • 一般用于处理类别间具有大小关系的数据
    • 编码后依然保留了大小关系

One-hot Encoding

独热编码:采用N位状态位对N个可能取值进行编码

  • 一般用于处理类别间不具有大小关系的特征

  • 独热编码后特征表达能力变差,特征的预测能力被人为拆分为多份

    • 通常只有部分维度是对分类、预测有帮助,需要借助特征选择降低维度
  • 在经典统计中,为避免完全多重共线性,状态位/哑变量会比取值数量少 1

优点

  • 能处理非数值属性
  • 一定程度上扩充了特征
  • 编码后向量时稀疏向量:可以使用向量的稀疏存储节省空间
  • 能够处理缺失值:高维映射方法中增加维度表示缺失

缺点

  • k-NN 算法:高维空间两点间距离难以有效衡量

  • 逻辑回归模型:参数数量随维度增加而增大,增加模型复杂度,容易出现过拟合

  • 决策树模型

    • 产生样本切分不平衡问题,切分增益非常小
      • 每个特征只有少量样本是 1,大量样本是 0
      • 较小的拆分样本集占总体比例太小,增益乘以所占比例之后几乎可以忽略
      • 较大拆分样本集的几乎就是原始样本集,增益几乎为 0
    • 影响决策树的学习
      • 决策树依赖数据统计信息,独热编码将数据切分到零散小空间上,统计信息不准确、学习效果差
      • 独热编码后特征表达能力边人为拆分,与其他特征竞争最优划分点失败,最终特征重要性会比实际值低

Binary Encoding

二进制编码:先用序号编码给每个类别赋予类别 ID,然后将类别 ID 对应二进制编码作为结果

  • 本质上利用二进制类别 ID 进行哈希映射,得到 0/1 特征向量
  • 特征维度小于独热编码,更节省存储空间

Weight of Evidence Encoding

WOE 编码:以分类变量各取值的 WOE 值作为编码值

  • $\%B_i, \%G_i$:分类变量取第 $i$ 值时,预测变量为 B 类、G 类占所有 B 类、G 类比例
  • $#B_i, #B_T$:分类变量取第 $i$ 值时,预测变量为 B 类占所有 B 类样本比例
  • $#G_i, #G_T$:分类变量取第 $i$ 值时,预测变量为 G 类占所有 G 类样本比例
  • WOE 编码是有监督的编码方式,可以衡量分类变量各取值中

    • B 类占所有 B 类样本比例、G 类占所有 G 类样本比例的差异
    • B 类、G 类比例,与所有样本中 B 类、G 类比例的差异
  • WOE 编码值能体现分类变量取值的预测能力,变量各取值 WOE 值方差越大,变量预测能力越强

    • WOE 越大,表明该取值对应的取 B 类可能性越大
    • WOE 越小,表明该取值对应的取 G 类可能性越大
    • WOE 接近 0,表明该取值预测能力弱,对应取 B 类、G 类可能性相近

优势

  • 相较于 one-hot 编码

    • 特征数量不会增加,同时避免特征过于稀疏、维度灾难
    • 避免特征筛选过程中,一部分特征取值被筛选,一部分被遗弃,造成特征不完整
    • 将特征规范到同一尺度的数值变量,同时也便于分析特征间相关性
  • LR 模型中,WOE 编码线性化赋予模型良好的解释性

    • WOE 编码本身即可反应特征各取值贡献
    • 可以用于给评分卡模型中各分箱评分

分类化/离散化:数值->分类

  • 分类型变量本质上无法建模,因为取值从含义上无法进行数值计算
  • 将数值型映射为分类型,往往只是中间步骤,最终会将分类型取值映射回数值型
  • 若分箱数量为 2,也被成为是二元化/布尔化

离散化综述

  • 模型使用离散特征、连续特征,是“海量离散特征+简单模型”、“少量连续特征+复杂模型”的权衡

    • 海量离散特征+简单模型:难点在于特征工程,成功经验可以推广,可以多人并行研究
    • 少量连续特征+复杂模型:难点在于模型调优,不需要复杂的特征工程
  • 一般的,连续特征对预测结果影响不会突变,合理的离散化不应造成大量信息丢失

    • 且若特征存在突变,模型将难以拟合(线性模型尤其)
    • 反而更应该离散化为多个分类特征,方便引入非线性
  • 事实上,根据Cover定理,离散化增加特征维度类似于投影至高维,更可能得到较优模型(也更容易过拟合)

    • 极限角度,对所有特征、取值均离散化,则可以得到完全可分模型(除特征完全一样分类不同)

描述角度

discretization_arch

  • supervised vs. unsupervised:是否使用分类信息指导离散化过程

    • 无监督
      • 如:等距、等频划分
      • 无法较好的处理异常值、不均匀分布
    • 有监督
      • 利用分类信息寻找合适切分点、间隔
      • 根据使用分类信息的方式有许多种
  • dynamic vs. static:离散化、分类是否同时进行

  • global vs. local:在特征空间的局部还是全局进行离散化

  • spliting vs. merging/top-down vs. bottom-up:自顶向下划分还是自底向上合并

  • direct vs. incremental:直接根据超参数确定分箱数量还是逐步改善直到中止准则

discretization_arch

典型过程

  • sort:排序
  • evaluate:评估分割点
  • split or merge:划分、合并
  • stop:停止离散化

discretization_steps

评价

  • Simplicity:可用切分点数量衡量简单性
  • Consistency:可以通过最小不一致数量衡量一致性
    • 不一致:样本具有相同的特征取值,但分类不同
    • 分箱最小不一致数量则为,箱内样本数量减最大类别数量
  • Accuracy:可通过分类器进行交叉验证的准确率衡量

优势

  • 方便工业应用、实现

    • 离散特征的增加、减少容易,方便模型迭代
    • 特征离散化处理缺失值、异常值更方便,可直接将其映射为某取值
    • 数值化后可指定取值类型,如:one-hot编码为为稀疏向量
      • 內积速度快
      • 存储方便
      • 容易扩展
  • 方便引入历史经验

    • 可以自由调整离散化结果,结合机器学习和历史经验得到最终的离散化结果
  • 模型更稳健

    • 模型不再拟合特征具体值,而是拟合某个概念,能够对抗数据扰动,更稳健
    • 对异常数据鲁棒性更好,降低模型过拟合风险
    • 某些场合需要拟合参数值更少,降低模型复杂度
  • (引入)非线性提升模型表达能力

    • 利用经验、其他信息将数值特征分段,相当于引入非线性,提升线性模型表达能力
    • 方便引入交叉特征,提升模型表达能力

适合场景

  • 离散化特征更适合 LR 等线性模型

    • 如下离散化优势:方便引入非线性等
    • 模型中所有特征都会被考虑,考虑细节、个体(包括 $L_1$ 范数也是被考虑后剔除)
  • GBDT 等树、抽样模型则不适合

    • 特征离散化后,由于抽样误差的存在,可能存在某些离散特征对样本预测能力非常强,非线性模型容易给这些特征更大权重,造成过拟合
      • 如:刚好抽取的 1000 个样本中某离散特征取值为 1 者全为正样本
    • 树模型每次使用一个特征划分节点,特征数量较多不利于模型训练
      • 若单个离散化特征预测能力不强,由于树深度限制,只有少量特征被作为划分依据,模型可能不收敛、表达能力更差
      • 若单个离散化特征预测能力强,连续特征也应该也有较好效果

无监督

  • 无监督分箱仅仅考虑特征自身数据结构,没有考虑特征与目标之间的关系

等频/等距/经验分箱

  • 分箱逻辑

    • 等频分箱:排序后按数量等分
      • 避免离散化后特征仍然为长尾分布、大量特征集中在少量组内
      • 对数据区分能力弱
    • 等距分箱:取值范围等分
    • 经验分箱
  • 分箱数量、边界超参需要人工指定

    • 根据业务领域经验指定
    • 根据模型指定:根据具体任务训练分箱之后的数据集,通过超参数搜索确定最优分桶数量、边界
  • 分箱经验、准则

    • 若组距过大,组内属性取值差距过大
      • 逻辑上分类不能够代表组内全部样本,组内取值影响可能完全不同
    • 若组距过小,组内样本过少
      • 随机性太强,不具备统计意义上说服力
      • 特征影响跳变过多

聚类分箱

  • K-Means 聚类
  • 层次聚类
  • 聚类过程中需要保证分箱有序

有监督

Binning:1R 分箱

  • 分箱逻辑、步骤
    • 将样本排序,从当前位置开始
      • 初始化:以允许的最少样本作为一箱,将箱内最多类别作为箱标签
      • 扩展:若下个样本类别与箱标签相同,则划至箱内
    • 重复以上,得到多个分箱
    • 将相邻具有相同标签的箱合并,得到最终分箱结果

Splitting

discretization_split

  • 基于信息熵的 split,具体划分依据如下
    • ID3:信息增益
    • C4.5:信息增益比
    • D2
    • Minimum Description Length Principle:描述长度

Merge分箱

discretization_merge

  • 基于依赖相关的 merge,具体划分依据如下
    • Chimerge:使用卡方值衡量两个相邻区间是否具有类似分布,若具有类似分布则将其合并
具体算法
  • 输入:目标分箱数量 $N$
  • 初始化
    • 将变量升序排列
    • 为减少计算量,若初始分箱数量大于阈值 $N_{max}$,则利用等频分箱进行粗分箱
    • 缺失值单独作为一个分箱
  • 合并区间
    • 计算每对相邻区间的卡方值
    • 将卡方值最小区间合并
    • 重复以上直至分箱数量不大于 $N$
  • 分箱后处理
    • 合并纯度为 1(只含有某类样本)的分箱
    • 删除某类样本占比超过 95% 的分箱
    • 若缺失值分箱各类样本占比同非缺失值分箱,则合并

评分卡模型

模型

  • 模型是策略的工具,策略包含模型,是模型的延伸

    • 相较于专家规则,机器学习模型
      • 允许加入更多特征维度,描述更加全面
      • 上限更高、下限更低
      • 涉及更多维度特征时,维护更方便
    • 机器学习模型和专家规则并非相互替代,更多的是串联
  • 业务问题转换为带解决数学问题

    • 尽量将业务问题转换为更容易解决分类问题而不是回归问题
    • 数学问题应尽量贴近业务:评估指标好不等于业务价值高
      • 远离业务问题的训练出模型,其线下评估效果好也不意味着上线效果好,如:针对客户而不是订单评价
      • 影响客户体验,如:客户等待时间预估偏低而不是偏高
  • 样本构造

    • 标签定义
      • 尽量为客观事实(是否、数量),而非主观判断(等级)
      • 样本粒度贴合实际、业务(订单粒度、客户粒度)
    • 样本数量
      • 二分类场景:正例样本大于 2000,占比超过 1%
    • 采样
      • 尽量不进行人工采样,保持训练数据正、负例比例和真实情况对齐

传统评分卡

评分卡 复杂学习
特征筛选 需筛选强特征,依赖业务经验 支持弱特征入模
特征处理 WOE 分箱,稳定性好
非线性 WOE 分箱提供非线性,解释性好 非线性充分挖掘数据信息,解释性差
复杂度 模型简单,泛化性好,样本需求小 模型复杂,表达能力强,样本少时容易过拟合
调参 超参少 调参难度大
模型提升方向 分(样本)群建模 Stacking 结合评分卡
  • 信用评分卡模型:利用模型将账户的属性特征按取值分组、并赋予一定分数,对账户进行信用评分

    • 最常见的金融风控手段之一,用于决定是否给予授信以及授信的额度和利率
    • 常用逻辑回归作为模型
    • 应用形式为查分组得分表、得分加和
      • 变量总是被分组,同组内得分相同
      • 用户属性变化不足以跨越箱边界,则得分不改变
  • 评分卡更关注得分相对值,即得分变动情况,评分绝对值含义意义不大

    • 常用 LRsigmoid 函数内线性函数结果作为初始得分
      • 根据 LR 意义,此时得分可以映射为账户的违约概率
    • 为美观,可能会对得分做线性变换
      • 常对各特征得分做放缩、对账户得分和做平移,此时放缩比例除以 $ln2$ 即为 PDO (对特征得分同时做等比例放缩、平移可行但蠢)
      • 线性变换后得分绝对值无意义,特征重要性可用特征各分组得分差距衡量
  • 评分卡在不同业务阶段体现的方式、功能不一样,按照借贷用户借贷时间可以分为

    • 申请评分卡 Application Score Card:贷前申请评分卡
    • 行为评分卡 Behavior Score Card:贷中行为评分卡
    • 催收评分卡 Collection Score Card:贷后催收评分卡

Stacking 评分卡

  • 考虑将评分卡、机器学习模型结合,使用机器学习模型构建特征,在此基础之上建立评分卡模型
  • Stacking 思想下的模型架构

    • 原始数据域
    • 数据挖掘、特征工程
    • 数据域特征子模型
    • 评分卡模型
  • 架构优势

    • 可解释性:保留在数据域粒度上的可解释性
    • 信息提取:子模型提取弱特征信息,降低特征工程门槛
    • 维度多样性:特征子模型机制,降低特征筛选必要性,保证各数据域都有特征入模
    • 模块化:具有良好扩展性,支持子模型替换、删除
    • 并行化:各数据域特征子模型专业、独立负责,提高效率
  • 架构劣势

    • 牺牲部分可解释性:若策略、模型使用相同变量,策略阈值调整对模型影响难以估计
      • 控制入模变量数目,便于快速定位
      • 利用 SHAPLIME 等工具解释模型
    • 增加上线、维护成本:需要上线多个模型,且对多个架构多个层次都进行监控
    • 协同建模增加对接成本
    • 分数据域特征子模型建模,容易造成数据孤岛,无法捕捉不同数据域间的数据联系
      • 跨数据域构造特征,构建跨数据域子模型

B 卡 - Behavior Scoring

贷中风控:根据借款人放贷后行为表现,预测未来逾期风险

  • B 卡用于动态监控放款后风险变化

    • 贷前阶段对借款人履约行为掌握少,且为静态数据
    • 一般无需实时,离线T+1计算即可
  • B 卡适合的信贷场景

    • 还款周期长
      • 长周期场景用户风险变化可能性大,与 A 卡形成区分
      • 引入贷中客户信息、还款履约行为,更准确识别客户逾期风险
    • 循环授信
      • 贷前阶段,无法很好识别客户风险,设置初始额度
      • 贷中与客户更多交互之后,可根据获取的贷中行为信息进行提额、降额操作
  • B 卡区分度一般很高

    • 除贷前数据之外,还可以使用账户的贷中表现数据
    • 特别的,不考虑排序性的情况下,使用是否逾期作为划分依据也能得到较高的 TPR-FPR,给出 KS 的下限
  • B 卡建模主要基于老客

    • 老客有足够长的申贷、还款记录
    • 新、老客定义口径
      • 新客:无历史结清订单
      • 老客:至少有1笔结清订单

C 卡 - Collection Scoring

贷后催收评分卡:当前状态为逾期情况下,预测未来出催可能性

  • 现阶段业界对 C 卡不够重视

    • 贷前风控最重要,优秀的贷前带来更容易的贷中、贷后
    • 催收效果和人员更相关,而逾期发生之后往往会委外
    • 随信贷行业的发展,贷后催收会趋向于精细化、专业化的发展,模型+策略的优化愈发重要
  • 模型分群

    • 新老入催用户
      • 首次入催
      • 再次入催
    • MOB 信息(数据厚薄)
      • 还款月份数
      • 催记月份数
    • 订单详情
      • 利率
      • 期限
      • 金额

样本选择

  • 建模样本窗口选择

    • 特征覆盖度:保证数据厚薄程度相同
    • 催收动作变化:出催没有大幅度变动
    • 客群变化:入催没有大幅变动
  • 同用户订单合案

    • 不合案:同用户多笔订单视为不同样本
      • 表现期内入催当期结清视为出催
    • 合案:同用户相近观察点入催订单合并
      • 表现期内入催当期所有账单还清视为出催
      • 对发生过 M2+ 逾期者,可将只要出催一期即视为出催

C 卡模型

  • 根据模型作用时间段分类
  • M1 全量模型:预测 M1 阶段(逾期 30 天内)还款概率

    • 样本:所有入催样本整体
      • 若缓催期内催出用户较多,则模型主要学习了缓催样本信息,约等于缓催响应模型,对非缓催样本效果较差
    • 时间窗口
      • 观察点:还款日
      • 表现期:M1 阶段
  • 缓催响应模型:预测适合缓催人群

    • 样本:需要积累足够的缓催响应样本
      • 若有足够缓催响应样本,可以和M1全量模型同时构建
      • 否则,在 M1 全量模型得分高(出催概率高)人群上进行 AB Test,积累缓催响应样本
    • 时间窗口
      • 观察点:还款日
      • 表现期:缓催响应日(2-3 天)
  • 贷后 N 天流转模型:预测贷后N天后的还款概率

    • 样本:缓催内未出催样本
      • 去除缓催样本影响,更多学习缓催期外出催样本信息
      • 优先对催出概率高的人群进行催收,提高出催概率
    • 时间窗口
      • 观察点:还款日(逾期)后 N
      • 表现期:至下个流转模型观察点、逾期阶段结束时间点
  • M2+ 模型:预测 M2+ 阶段的还款概率(类似贷后流转模型)

    • 样本:M1 阶段未出催样本
    • 时间窗口
      • 观察点:M2 阶段起始
      • 表现期:至下个流转模型观察点、逾期阶段结束时间点

模型应用方法

  • 缓催响应人群确定

    • 交叉 M1 模型、缓催响应模型,根据模型交叉结果设置阈值
    • 根据阈值筛选缓催响应人群
    • 限定缓催期(2-3 天),将缓催响应样本分为人工催收、缓催两组,观察两组在缓催期限内出催率变化
      • 若出催率相同,则认为缓催响应人群分析方法可行,对缓催响应人群可采取缓催策略
      • 若出催率相差较大,则调整缓催响应人群分析方法
    • 缓催模型响应时间(缓催期)可根据响应时间段内的出催率变化设置
  • 模型搭建策略

    • M1 阶段出催概率较大,在M1阶段会设计多个细分模型
      • 至少:M1 阶段全量模型
      • 缓催样本足够
        • 缓催响应模型
        • 贷后 N 天流转模型
      • 精细化管理:多个不同时间窗口的贷后流转模型
    • M2+ 阶段根据样本量、精细化程度设置适量模型

开发流程标准化

  • 风控模型开发流程标准化意义
    • 提高建模效率:可批量快速生产模型,提高效率
    • 帮助理解指标逻辑、业务含义,利于调试优化
    • 流程规范约束
      • 统一建模流程,减少出错概率、便于问题回溯
      • 统一命名方式,便于汇总文档

数据预处理

特征编码

  • 特征离散化

  • WOE 编码特征

    • WOE 曲线应符合业务逻辑(一般单调),并且经过跨时间 窗口验证,否则应该调整
    • LR 模型中特征权重应该全为正值,否则
      • 同数据 WOE 值体现的逻辑相违背
      • 负值权重特征存在较严重共线性
  • one-hot 编码特征

    • 同特征下个分箱单独作为独立变量取值
      • 权重灵活性更大,模型效果可能较好
      • 变量数量多,需要样本数量大,模型效果可能较差(随机解法)
    • 各特征分箱之间无联系,难以通过模型剔除某个变量

样本赋权

  • 样本赋权:充分利用所有样本的信息,避免样本有偏
    • 按样本距今时间赋权,近期样本高权重
    • 按业务特性赋权,不同额度、利率、期限不同权重
    • 按账户类型赋权

拒绝推断

  • Reject Inference 拒绝推断:避免样本偏差导致模型估计过于乐观

Exploratory Data Analysis

  • 风控领域样本较少,一般按月粒度观察,即将样本按月分组为 vintage 进行分析,探索、评估数据

    • 稳定性
    • 信息量
    • 信息重复/相关性
  • 实操中可逐阶段设置多组阈值,分布进行变量探索、筛选

    • 多组阈值逐步剔除能尽可能保留高信息量特征
    • 避免相关性、RF 特征重要度等 非单变量指标 剔除过多特征

模型评估

  • 有效性/区分度

    • GINI 指数
    • KS
    • 坏样本率:组内、累计
    • 提升度 = 召回样本坏样本率 / 全部样本坏样本率
    • odds = 坏样本率 / 好样本率
  • 排序性

    • AUC 值/ROC 曲线
  • 稳定性

    • PSI
    • Vintage 内坏占比、Lift 值、odds 等指标稳定性
  • 模型得分展示表

    • 箱内样本数
    • 好、坏样本数
    • 箱内坏样本、比例
    • 累计好、坏样本
    • 累计好、坏样本比例:TPRFPRTPR-FPR
    • 累计通过率、坏样本比例

模型应用

Calibration 模型校准

  • 一致性校准:将模型预测概率校准到真实概率
  • 尺度变换:将风险概率转换为整数分数

导出得分

  • 原始得分

    • one-hot 编码:LR 模型系数
    • WOE 编码:LR 模型系数(权重)、WOE 值之积
  • 常对各特征得分做放缩、对账户得分和做平移

    • PDO:违约翻倍得分
      • 用于缩放原始得分
      • 得分按 $\frac {PDO} {ln2}$ 缩放后,得分减少 $PDO$ 分,用户违约 odds 翻倍,缺省即 $ln2$
    • 账户得分总和平移则仅仅是为了美观
    • 对特征得分同时做等比例放缩、平移可行但蠢

Word2Vec

Word2Vec

Word2Vec:word embeding的一种,使用层次化softmax、负采样 训练词向量

Hierarchical Softmax

层次Softmax

word2vec_hierarchical_softmax

  • 对所有词向量求和取平均作为输入层到隐层的映射 (特指CBOW模型)

  • 使用霍夫曼树代替从隐藏层到输出softmax层的映射

思想

  • softmax需要对$m$个类别求出softmax概率,参数多、计算复杂

  • 考虑将$m$个类别划分为多个二分类sigmoid,即

    • 将总类别划分为两组
    • 依次判断数据点属于哪组
    • 直至数据点所属组仅包含一个类别
  • 则多个sigmoid划分构成一棵二叉树,树叶子节点即为$m$ 类别

    • 二叉树结构可以由多种,最优二叉树应该使得对整个 数据集而言,sigmoid判断次数最少
    • 即应该使用按照数据点频数构建的霍夫曼树
    • 霍夫曼树

模型

  • 输入$x^T$所属类别霍夫曼编码为$d={d_1,\cdots,d_M}$, 则应最大化如下似然函数

    • $w_j, b_j$:节点$j$对应sigmoid参数
    • $P(d_i)$:以sigmoid激活值作为正例概率 (也可以其作为负例概率,但似然函数需更改)
  • 则对数似然函数为

梯度计算

  • 则参数$w_{j_M}$梯度如下

  • 词向量$x$梯度如下

CBOW流程

  • 特征词周围上下文词均使用梯度更新,更新输入
  • 基于预料训练样本建立霍夫曼树
  • 随机初始化模型参数$w$、词向量$w$
  • 对训练集中每个样本 $(context(x), x)$($2C$个上下文)如下 计算,直至收敛

    • 置:$e=0, xw=\frac 1 {2C} \sum{c=1}^{2C} x_c$

    • 对$x$的霍夫曼编码 $d={d_1, \cdots, d_M}$ 中 $d_i$ 计算

    • 更新 $2C$ 上下文词对应词向量

Skip-Gram流程

  • 考虑上下文是相互的,则 $P(x{context}|x)$ 最大化时,$P(x|x{context})$ 也最大
  • 为在迭代窗口(样本)内更新仅可能多词向量,应该最大化 $P(x|x_{context})$,使用梯度更新上下文 $2C$ 个词向量,更新输出(条件概率中更新条件)
  • 基于预料训练样本建立霍夫曼树
  • 随机初始化模型参数 $w$、词向量 $w$
  • 对训练集中每个样本 $(x, context(x))$、每个样本中上下文词向量 $x_c$($2C$ 个上下文),训练直至收敛

    • 置:$e=0$

    • 对 $x$ 的霍夫曼编码 $d={d_1, \cdots, d_M}$ 中 $d_i$ 计算

    • 更新 $2C$ 上下文词对应词向量

Negtive Sampling

负采样

思想

  • 通过负采样得到$neg$个负例
  • 对正例、负采样负例建立二元逻辑回归

模型、梯度

  • 对类别为$j$正例、负采样负例应有如下似然函数、对数似然 函数

    • $y_i$:样本点标签,$y_0$为正例、其余负例
  • 同普通LR二分类,得到参数、词向量梯度

负采样方法

  • 每个词对应采样概率为词频取$3/4$次幂后加权

CBOW流程

  • 随机初始化所有模型参数、词向量
  • 对每个训练样本$(context(x_0), x_0)$负采样$neg$个中心词 $x_i$,考虑$x_0$为类别$j$
  • 在以上训练集$context(x0), x_0, x_1, \cdots, x{neg}$中 训练直至收敛

    • 置:$e=0, xw=\frac 1 {2C} \sum{c=1}^{2C} x_c$

    • 对样本$x0, x_1, \cdots, x{neg}$,计算

    • 更新$2C$上下文词对应词向量

Skip-gram中心词

  • 类似Hierarchical Softmax思想,更新输出$2C$个词向量
  • 随机初始化所有模型参数、词向量
  • 对每个训练样本$(context(x_0), x_0)$负采样$neg$个中心词 $x_i$,考虑$x_0$为类别$j$
  • 以上训练集$context(x0), x_0, x_1, \cdots, x{neg}$中, 对每个上下文词向量$x_c$如下训练直至收敛

    • 置:$e=0$

    • 更新$2C$上下文词对应词向量

Pandoc介绍

Pandoc

Pandoc:将文本在不同标记语言之间相互转换的工具

  • Pandoc使用Haskell开发,最新版本Pandoc可以使用Haskell平台 包管理器cabal安装

    1
    2
    3
    $ sudo apt install haskell-platform
    $ cabal update
    $ cabal install pandoc
  • Pandoc支持的标记语言格式包括 (具体可通过--list-input-formats查看)

    • Markdown
    • ReStructuredText
    • HTML
    • LaTeX
    • ePub
    • MS Word Docx
    • PDF
  • Pandoc输入、输出文本

    • 可从输入、输出的文件扩展名推测输入、输出格式
    • 缺省输入格式为Markdown、缺省输出格式为html
    • 若未指明输入、输出文件,则读取、写入标准输入、输出
    • 文本默认为utf-8编码,否则可以用iconv转换文本 编码进行输入、输出

Pandoc相关选项

基础选项

  • -f/-t:输入、输出格式
  • -o:输出文件
  • --number-sections/-N:为标题添加编号
  • --verbose:详细调试信息
    • 其中会给出资源文件目录~/.pandoc,存放模板等
  • --log:日志信息
  • --file-scope:分别转换每个文件
    • 指定多个输入文件时默认将多个文件拼接,空行分隔

信息选项

  • --list-input-formats/--list-output-formats:输入、 输出格式
  • --list-extensions[=FORMAT]:列出Markdown扩展支持情况
    • <FORMAT>-<EXT>可以增减格式中一个、多个扩展选项
  • --list-highlight-languages:语法高亮支持语言
  • --list-highlight-styles:语法高亮支持样式

模板选项

  • -standalone/-s:生成完整文件 (仅对可生成片段的某些格式:html、LaTeX)
    • 默认生成文档片段
    • 采用相应内值模板生成完整文件
  • --print-default-template=<FORMAT>/-D <FORMAT>:输出 对应格式的默认模板
  • --template=<TPL>:指定创建文档所需模板
  • --css=<URL>/-c <URL>:指定CSS样式表

自定义值传递

  • --variable=<KEY[:<VAL>]>/-V <KEY[:<VAL>]>:指定模板 变量取值
  • --metadata=<KEY[:<VAL>]>/-M <KEY[:<VAL>]>:指定 元数据字段取值
    • 元数据字段影响模板变量取值,同时影响底层文档元数据
  • --metadata-file=<FILE>:从YAML格式文件中设置元数据字段 取值

PDF生成相关

  • --toc:生成目录
  • --template=<TPL>:编译使用的LaTeX模板,缺省为自带
  • --latex-engine=<ENG>:指定LaTeX引擎,需安装
    • 默认pdflatex对中文支持缺失,建议使用xelatex
  • --highlight-style=<STY>:代码块语法高亮
    • 自带高亮样式可通过--list-highlight-styles查看
    • 也可指定高亮样式文件
  • --listings:LeTeX文档中使用Listings包格式化代码块
  • --biblatex/--natbib:指定处理参考文献程序
  • --bibliography=<FILE>:设置文档元数据中参考文献信息
  • -f markdown-implicit_figures设置Markdown格式,指定 图像保持原始位置,避免pdf中错位

HTML生成相关

  • --self-contained:将css、图片等所有外部文件压缩进html 文件中
  • Markdown中使用html标记可以在转换为html后保留,可以据此 设置转换后的样式

DOCX生成相关

  • --reference-doc=<FILE>:指定格式参考文件
    • 参考文件内容被忽略,就样式、文档属性被使用
  • --print-default-data-file=reference.docx:输出系统默认 模板
  • --extract-media=<DIR>:提取文档中多媒体文件至文件夹, 并在目标文件中设置对其引用

EPUB生成相关

  • --epub-cover-image=<FILE>
  • --epub-metadata=<FILE>
  • --epub-embed-font=<FILE>

数学公式渲染

  • --mathjax[=<URL>]
  • --mathml
  • --katex[=<URL>]

Pandoc模板

模板变量

模板变量:Pandoc模板中可以包含变量用于自定义模板

1
2
3
4
5
6
7
8
9
10
11
$title$						# 变量表示方法

$if(var)$ # 变量条件语句
X
$else$
Y
$endif$

$for(var)$ # 变量循环语句
X
$endfor$
  • 变量赋值方式
    • 命令行参数提供
    • 文档元数据中查找