first-commit:1.0

This commit is contained in:
Tang Wuguo 2023-08-24 20:54:12 +08:00
parent d9162144bb
commit 8bc17711c1
26 changed files with 339281 additions and 0 deletions

100
UserPortrait/README.md Normal file
View File

@ -0,0 +1,100 @@
# openGauss用户操作模型
*Tang Wuguo, tangwg@csu.edu.cn*
本demo使用openGauss中的[AI模块](https://docs.opengauss.org/zh/docs/5.0.0/docs/AIFeatureGuide/%E5%8E%9F%E7%94%9FDB4AI%E5%BC%95%E6%93%8E.html)以及数据库产生的审计日志,对数据库用户的操作进行建模并进行用户画像。
在openGauss中提供了完善的日志系统其中的审计日志中就记录了用户每次的操作日志中每一条记录表示: 时间t用户A对客体B进行了操作C。首先对原始的审计日志进行筛选过滤去除噪声记录然后使用特征工程设计了基于统计学上的特征。最后让AI模型使用前面的特征自动学习数据更深层的特征。最后模型具有识别不同类型用户的能力。
![用户操作模型](https://cdn.nlark.com/yuque/0/2023/png/21528568/1689931339917-568c1737-b9d2-4e40-b013-776b846ec967.png#averageHue=%23f9f7f4&clientId=u862145d8-5212-4&from=paste&id=uc944a0f2&originHeight=302&originWidth=1217&originalType=url&ratio=1&rotation=0&showTitle=true&size=44504&status=done&style=none&taskId=u1ea3bcff-0c69-4d9d-91db-57306164042&title=%E7%94%A8%E6%88%B7%E6%93%8D%E4%BD%9C%E6%A8%A1%E5%9E%8B "用户操作模型")
## 1.文件说明🚀
- **code1-SimulateAuditLog**
由于没有找到公开的openGauss审计日志所以在这里我使用python脚本模拟了一个教务管理系统场景下的审计日志
- `\code\1.1_simulateOperation.ipynb`
模拟了教务管理系统中4种用户(学生端、教师端、教务处、管理员)的操作
- `\code\1.2_analyzeLog.ipynb`
对产生的审计日志进行分析其频数分布,包括:操作的主体即用户,操作客体,操作类型,涉及到的数据库
- `\data`
- `xxx.csv`: 包括模拟操作需要的源数据(学生、教师信息、课程表等)
- `hist_xxx.jpg`: 分析日志保存的分布图
- `audit_log_202307101107.csv`: 经过模拟操作得到的审计日志
- `log_01.csv`: 初步筛选后得到的日志
- **code2-UserPortraitModel**
用户操作建模&用户画像
- `2.1FeatureEngineering.sql`
收集&筛选日志,构建特征
- `2.2UserPortraitModel.sql`
训练、评估模型, 用户画像, 识别危险用户
- **data**
- `audit_log.csv`: 审计日志
- `log_01.csv` 过滤后的日志
- `log_train.csv`: 训练集
- `log_test.csv`: 测试集
- `log_test2.csv`: 测试集2, 用来测试识别危险用户
- **doc**
- `设计文档.md `
- `审计日志调研.md`
- `AI模块调研.md`
## 2.如何在自己的数据库上进行用户画像✨
前面所构建的用户操作模型是在模拟的教务管理系统的日志上进行开发的模型肯定无法适用各种用户场景但是方式是通用的。所以在这个部分将介绍如何修改SQL脚本来在你的数据库上适用自己的审计日志构建用户画像。
需要修改的代码都在**code2-UserPortraitModel**目录下,步骤如下:
### 配置审计日志
虽然openGauss中默认打开了审计日志但是记录日志需要额外的存储所以并不会对所有用户的操作都会记录所以需要对审计项目进行配置例如下面的代码打开了对表修改和表查询的审计项
```
# 检查audit_dml_state配置值
cat /var/lib/opengauss/data/postgresql.conf | grep audit_dml_state
# 修改配置
gs_guc set -D /var/lib/opengauss/data -c "audit_dml_state=1"
gs_guc set -D /var/lib/opengauss/data -c "audit_dml_state_select=1"
# 重启数据库使参数生效
gs_ctl restart -D /var/lib/opengauss/data
```
### 修改FeatureEngineering.sql
**1. 收集日志**
这里需要筛选出你需要的日志数据,需要修改的地方有日志的起止时间、感兴趣的用户
**2. 提取特征**
- 代码中统计了用户每天对各种类型的操作次数作为特征,每种操作的统计都存在一个视图中,操作的类型分为:**登录、系统级、数据库级、表级**。
- 对于前面3类的特征应该保留重点需要修改对**表级别的操作特征提取**,考虑自己的系统中各个**用户的操作特点**来设计表级别的操作特征。
- 例如在教务系统中,对学生端和教师端分别对学生表和教师表查询得多,教师端还还会对成绩表进行增改操作,教务处则是维护学生信息、教师信息。所以我在设计特征的时候,统计了这些表的查询次数、修改次数。因为这些统计信息可以将各类型的用户区别开来。
- 最后,在这一步设计的特征可以尽可能的多些,因为在后面训练模型时还可以选择哪些特征要输入到模型中
**3. 汇总特征**
这个部分将上一步所有的特征视图汇总到一张表中,这个表基本就是我们用来训练模型的数据集了
### 修改UserPortraitModel.sql
**1. 整理数据**
为了能让数据适用openGauss中需要的模式需要对数据进行一些修改给每条数据标号、将特征值转换成双精度浮点型
**2. 创建模型**
这个部分用到的是使用logistic_regression作为基础分类器的多分类模型
这里应该修改的有:
- 输入模型的特征FEATURES关键字
- 模型的[超参数](https://docs.opengauss.org/zh/docs/5.0.0/docs/AIFeatureGuide/%E5%8E%9F%E7%94%9FDB4AI%E5%BC%95%E6%93%8E.html#:~:text=GD%3A-,logistic_regression,-%E3%80%81linear_regression%E3%80%81svm_classification%E3%80%81pca)WITH关键字部分
**3. 模型预测**
使用上一步训练好的模型进行预测,注意这里用于预测的特征应该与前面用于训练的特征要一致
**4. 模型评估**
计算模型分类的准确率作为评估模型的指标,我们如果准确率不高,应该回到第二步构建模型的时候调整参数,或是输入模型的特征数
**5. 模型应用**
- 用户画像
设置用户画像标签
设置赋予标签的阈值,例如在代码中设置的是下四分位值作为阈值
- 识别危险用户
和模型预测这部分类似,需要修改模型接收的特征

View File

@ -0,0 +1,323 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import psycopg2 as pg\n",
"import random"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# 获取DB连接对象\n",
"4个用户 \\\n",
" pm: create 用户 数据库 \\\n",
" t_coder: 老师端, select 教师表, insert\\update 成绩表 \\\n",
" s_coder: 学生端, select 学生表 课程表 成绩表 \\ \n",
" affair: 教务处, insert 教师表 学生表 课程表 \\"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"def get_conn(user='pm'):\n",
" conn = pg.connect(database=\"db_school\", \n",
" user=user, \n",
" password=\"123456twG\", \n",
" host=\"127.0.0.1\", port=\"7654\")\n",
" return conn"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# 从CSV文件导入\n",
"Day1, affair用户, 将student.tsv, teacher.tsv, course.tsv导入到数据库中"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def insert_tsv(conn, table_name, tsv_path, sep='\\t'):\n",
" print(\"Opened database successfully\")\n",
" cur = conn.cursor()\n",
"\n",
" copy_query = f\"COPY public.{table_name} FROM STDIN WITH (FORMAT CSV, HEADER TRUE, DELIMITER E'\\\\t', ENCODING 'UTF-8')\"\n",
" with open(tsv_path, 'r', encoding='utf-8') as file:\n",
" cur.copy_expert(copy_query, file)\n",
" print(f\"Copy {tsv_path} ok\")\n",
" # 提交事务\n",
" conn.commit()\n",
" # 关闭数据库连接\n",
" cur.close()\n",
" conn.close()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"conn = get_conn(user='affair')\n",
"insert_tsv(conn, \"teacher\", '../data/teacher.tsv')"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Opened database successfully\n",
"Copy ../data/student.tsv ok\n",
"Opened database successfully\n",
"Copy ../data/course.tsv ok\n"
]
}
],
"source": [
"conn2 = get_conn(user='affair')\n",
"conn3 = get_conn(user='affair')\n",
"insert_tsv(conn2, \"student\", '../data/student.tsv')\n",
"insert_tsv(conn3, \"course\", '../data/course.tsv')"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# 向score添加数据\n",
"Day2, t_coder用户向score表插入数据"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"def insert_score(conn):\n",
" student_id = range(20230001, 20230041)\n",
" course_id = range(10, 15)\n",
" cur = conn.cursor()\n",
" \n",
" insert_value = []\n",
" for s_id in student_id:\n",
" for c_id in course_id:\n",
" random_score = random.randint(75, 95)\n",
" insert_value.append((s_id, c_id, random_score))\n",
"\n",
" insert_sql = \"INSERT INTO public.score (student_id, course_id, score) VALUES (%s, %s, %s)\"\n",
" cur.executemany(insert_sql, insert_value)\n",
" conn.commit()\n",
" cur.close()\n",
" conn.close()\n",
" \n",
"\n",
" \n",
"# 修改成绩\n",
"def update_score(conn, repete_times=10):\n",
" cur = conn.cursor() \n",
" \n",
" update_value = []\n",
" for i in range(repete_times):\n",
" s_id = random.randint(20230001, 20230040)\n",
" c_id = random.randint(10, 14)\n",
" det_score = random.randint(-5, 0)\n",
" update_value.append((det_score, s_id, c_id))\n",
" \n",
" update_sql = \"UPDATE public.score SET score = score+(%s) WHERE student_id=(%s) AND course_id=(%s) \"\n",
" cur.executemany(update_sql, update_value)\n",
" conn.commit()\n",
" cur.close()\n",
" conn.close()"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"conn = get_conn(user='t_coder')\n",
"insert_score(conn)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"conn_update_score = get_conn(user='t_coder')\n",
"update_score(conn_update_score)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# 查询teacher,student,score\n",
"Day1-5: t_coder查询teacher表s_coder查询student表、course表 \\\n",
"Day2: teacher查询score, 学生查询score"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"# 对表{table_name}指定{query_id}查询{repete_times}次\n",
"# 查询student teacher course都是调用这个函数\n",
"def select(conn, table_name, repete_times, query_id, id_range):\n",
" cur = conn.cursor() \n",
" query_value = [] \n",
" for t in range(repete_times):\n",
" query_value.append((random.randint(id_range[0], id_range[1]),))\n",
" query_sql = f\"SELECT * FROM public.{table_name} WHERE {query_id}=(%s)\"\n",
" \n",
" cur.executemany(query_sql, query_value)\n",
" conn.commit()\n",
" cur.close()\n",
" conn.close()\n",
" \n",
"\n",
"# 学生查询课表\n",
"def select_course(conn, repete_times):\n",
" cur = conn.cursor() \n",
" query_sql = \"SELECT course_id, cname, name, point FROM public.course c, public.teacher t where c.teacher_id = t.teacher_id\" \n",
" \n",
" for i in range(repete_times):\n",
" cur.execute(query_sql)\n",
" result = cur.fetchall()\n",
" conn.commit()\n",
" cur.close()\n",
" conn.close()\n",
"\n",
"\n",
"# 学生查询成绩\n",
"def select_score(conn):\n",
" cur = conn.cursor() \n",
" query_sql = \"\"\"SELECT s.student_id, s.\"name\", c.cname, s2.score \n",
" FROM public.score s2, public.course c, public.student s \n",
" WHERE s2.course_id = c.course_id AND s2.student_id = s.student_id AND s.student_id=(%s)\"\"\" \n",
" query_value = [(i,) for i in range(20230001, 20230041)]\n",
" \n",
" cur.executemany(query_sql, query_value) \n",
" conn.commit()\n",
" cur.close()\n",
" conn.close() \n",
"\n",
"\n",
"# 老师查询指定课程的所有成绩\n",
"def select_score_teacher(conn):\n",
" cur = conn.cursor() \n",
" query_value = [(i,) for i in range(10, 15)]\n",
" query_sql = \"SELECT * FROM public.score WHERE course_id=(%s) ORDER BY score desc;\"\n",
" \n",
" cur.executemany(query_sql, query_value)\n",
" conn.commit()\n",
" cur.close()\n",
" conn.close()\n",
" "
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"执行查询学生信息、老师信息、课程信息脚本"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"conn_s = get_conn(user='s_coder')\n",
"conn_s2 = get_conn(user='s_coder')\n",
"conn_t = get_conn(user='t_coder')\n",
"\n",
"\n",
"s_repete = random.randint(70,100)\n",
"s2_repete = s_repete + random.randint(5,10)\n",
"t_repete = random.randint(10,30)\n",
"\n",
"# s_coder查询学生信息\n",
"select(conn_s, \"student\", s_repete, \"student_id\", (20230001, 20230040))\n",
"# s_coder查询课程信息\n",
"select_course(conn_s2, s2_repete)\n",
"# t_coder查询教师信息\n",
"select(conn_t, \"teacher\", t_repete, \"teacher_id\", (101, 105))"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"查询成绩"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"conn_score = get_conn(user='s_coder')\n",
"conn_score_t = get_conn(user='t_coder')\n",
"\n",
"# s_coder查询成绩\n",
"select_score(conn_score)\n",
"# t_coder按照课程号查询成绩\n",
"select_score_teacher(conn_score_t)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "opengauss",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.16"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
course_id teacher_id cname point
10 101 C语言程序设计 2
11 102 数据结构 3
12 103 数据库 3
13 104 操作系统 3
14 105 计算机网络 2
1 course_id teacher_id cname point
2 10 101 C语言程序设计 2
3 11 102 数据结构 3
4 12 103 数据库 3
5 13 104 操作系统 3
6 14 105 计算机网络 2

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

View File

@ -0,0 +1,41 @@
student_id name class
20230001 张伟 2301
20230002 王芳 2301
20230003 李娜 2301
20230004 张磊 2301
20230005 王伟 2301
20230006 李秀英 2301
20230007 王敏 2301
20230008 李强 2301
20230009 张静 2301
20230010 王勇 2301
20230011 李霞 2301
20230012 张涛 2301
20230013 王静 2301
20230014 李军 2301
20230015 张敏 2301
20230016 王磊 2301
20230017 李丽 2301
20230018 张洋 2301
20230019 王艳 2301
20230020 李明 2301
20230021 张梅 2302
20230022 王强 2302
20230023 李红 2302
20230024 张宇 2302
20230025 王欣 2302
20230026 李杰 2302
20230027 张婷 2302
20230028 王辉 2302
20230029 李静 2302
20230030 张刚 2302
20230031 王娜 2302
20230032 李峰 2302
20230033 张萍 2302
20230034 王超 2302
20230035 李莉 2302
20230036 张鹏 2302
20230037 王桂芳 2302
20230038 李飞 2302
20230039 张霞 2302
20230040 王建国 2302
1 student_id name class
2 20230001 张伟 2301
3 20230002 王芳 2301
4 20230003 李娜 2301
5 20230004 张磊 2301
6 20230005 王伟 2301
7 20230006 李秀英 2301
8 20230007 王敏 2301
9 20230008 李强 2301
10 20230009 张静 2301
11 20230010 王勇 2301
12 20230011 李霞 2301
13 20230012 张涛 2301
14 20230013 王静 2301
15 20230014 李军 2301
16 20230015 张敏 2301
17 20230016 王磊 2301
18 20230017 李丽 2301
19 20230018 张洋 2301
20 20230019 王艳 2301
21 20230020 李明 2301
22 20230021 张梅 2302
23 20230022 王强 2302
24 20230023 李红 2302
25 20230024 张宇 2302
26 20230025 王欣 2302
27 20230026 李杰 2302
28 20230027 张婷 2302
29 20230028 王辉 2302
30 20230029 李静 2302
31 20230030 张刚 2302
32 20230031 王娜 2302
33 20230032 李峰 2302
34 20230033 张萍 2302
35 20230034 王超 2302
36 20230035 李莉 2302
37 20230036 张鹏 2302
38 20230037 王桂芳 2302
39 20230038 李飞 2302
40 20230039 张霞 2302
41 20230040 王建国 2302

View File

@ -0,0 +1,6 @@
teacher_id name
101 张老师
102 王老师
103 李老师
104 赵老师
105 刘老师
1 teacher_id name
2 101 张老师
3 102 王老师
4 103 李老师
5 104 赵老师
6 105 刘老师

View File

@ -0,0 +1,105 @@
-- 在本脚本中将实现从openGauss中收集审计日志并从审计日志中提取用户操作特征
-- Tang Wuguo, 2023-7-24, tangwg@csu.edu.cn
-- 1. 收集日志
CREATE SCHEMA pm;
SELECT * INTO pm.audit_log
FROM pg_query_audit('2023-07-05 02:01:53','2023-07-09 11:00:00');
CREATE OR REPLACE VIEW pm.log_01 as
select to_char(al."time", 'YYYY-MM-DD') as date,
al."type", al."result" , al."userid" ,
al."username" , al."database", al."client_conninfo" ,
al."object_name", al."detail_info"
FROM pm.audit_log al
where username is not null and username not in ('opengauss','[unknown]');
-- 2. 提取各类特征
-- Count user login times per day
CREATE OR REPLACE VIEW pm.query_login as select "date", l.username, coalesce(count(*),0) as "login_count"
from pm.log_01 l
where l."type" in ('login_success')
group by "date", l.username
order by "date", "login_count" desc;
-- Count sys opetate
CREATE OR REPLACE VIEW pm.quey_sys as select l."date", l.username, coalesce(count(*),0) as "sys_count"
from pm.log_01 l
where l."type" in ('set_parameter', 'internal_event', 'grant_role', 'ddl_user')
group by l."date", l.username
order by l."date", "sys_count" desc;
-- Count user db operate times per day
CREATE OR REPLACE VIEW pm.query_db as select l."date", l.username, coalesce(count(*),0) as "db_count"
from pm.log_01 l
where l."type" in ('ddl_database', 'ddl_schema', 'ddl_table')
group by l."date", l.username
order by l."date", "db_count" desc;
-- Tabel insert
CREATE OR REPLACE VIEW pm.query_insert_all as select l."date", l.username, coalesce(count(*),0) as "table_count"
from pm.log_01 l
where l."type" in ('dml_action', 'copy_from')
group by l."date", l.username
order by l."date", "table_count" desc;
-- Score insert
CREATE OR REPLACE VIEW pm.query_insert_score as select l."date", l.username, coalesce(count(*),0) as "table_count"
from pm.log_01 l
where l."type" in ('dml_action', 'copy_from') and object_name in ('score')
group by l."date", l.username
order by l."date", "table_count" desc;
-- Student insert
CREATE OR REPLACE VIEW pm.query_insert_student as select l."date", l.username, coalesce(count(*),0) as "table_count"
from pm.log_01 l
where l."type" in ('dml_action', 'copy_from') and object_name in ('student')
group by l."date", l.username
order by l."date", "table_count" desc;
-- Teacher insert
CREATE OR REPLACE VIEW pm.query_insert_teacher as select l."date", l.username, coalesce(count(*),0) as "table_count"
from pm.log_01 l
where l."type" in ('dml_action', 'copy_from') and object_name in ('teacher')
group by l."date", l.username
order by l."date", "table_count" desc;
-- Table select
CREATE OR REPLACE VIEW pm.query_sel_info as select l."date", l.username, coalesce(count(*),0) as "tab_sel_uinfo_count"
from pm.log_01 l
where l."type" in ('dml_action_select') and l.object_name in ('student', 'teacher', 'course')
group by l."date", l.username
order by l."date", "tab_sel_uinfo_count" desc;
-- Score select
CREATE OR REPLACE VIEW pm.query_sel_score as select l."date", l.username, coalesce(count(*),0) as "tab_sel_score_count"
from pm.log_01 l
where l."type" in ('dml_action_select') and l.object_name in ('score')
group by l."date", l.username
order by l."date", "tab_sel_score_count" desc;
-- 3. 汇总所有特征
-- Merge All Sub-view
CREATE OR REPLACE VIEW pm.log_refined as
select t1.date, t1.username,
COALESCE(t1.login_count, 0) as login,
COALESCE(t2.sys_count, 0) as sys,
COALESCE(t3.db_count, 0) as db,
COALESCE(t4.table_count, 0) as insert_all,
COALESCE(t5.table_count, 0) as insert_score,
COALESCE(t6.table_count, 0) as insert_student,
COALESCE(t7.table_count, 0) as insert_teacher,
COALESCE(t8.tab_sel_uinfo_count, 0) as sel_info,
COALESCE(t9.tab_sel_score_count, 0) as sel_score
FROM pm.query_login t1
LEFT JOIN pm.query_sys t2 ON t1."date" = t2."date" AND t1.username = t2.username
LEFT JOIN pm.query_db t3 ON t1."date" = t3."date" AND t1.username = t3.username
LEFT JOIN pm.query_insert_all t4 ON t1."date" = t4."date" AND t1.username = t4.username
LEFT JOIN pm.query_insert_score t5 ON t1."date" = t5."date" AND t1.username = t5.username
LEFT JOIN pm.query_insert_student t6 ON t1."date" = t6."date" AND t1.username = t6.username
LEFT JOIN pm.query_insert_teacher t7 ON t1."date" = t7."date" AND t1.username = t7.username
LEFT JOIN pm.query_sel_info t8 ON t1."date" = t8."date" AND t1.username = t8.username
LEFT JOIN pm.query_sel_score t9 ON t1."date" = t9."date" AND t1.username = t9.username
ORDER BY t1.date, t1.username;

View File

@ -0,0 +1,107 @@
-- 在这里我们将使用openGauss中的DB4AI模块对用户的画像
-- 需要注意只有企业版的openGauss才具有AI特性如果是轻量版请安装最新的企业版软件
-- 1. 整理数据
-- 为了适应模型的输入要求,需要对原始数据进行进一步修改
CREATE OR REPLACE VIEW pm.log_train as
SELECT row_number() OVER () AS id, "date", username,
dense_rank() OVER (ORDER BY username) AS name_id,
login::double precision, sys::double precision, db::double precision,
insert_all::double precision, insert_score::double precision, insert_student::double precision, insert_teacher::double precision,
sel_info::double precision, sel_score::double precision
FROM log_refined
ORDER BY log_refined."date", dense_rank() OVER (ORDER BY log_refined.username);
-- 2. 创建模型
-- 模型m0没有使用insert_student, insert_teacher特征
CREATE MODEL log_m0
USING multiclass
FEATURES login, sys, db, insert_all, insert_score, sel_info, sel_score
TARGET name_id
FROM log_train
WITH classifier="logistic_regression";
-- 模型m1
CREATE MODEL log_m1
USING multiclass
FEATURES login, sys, db, insert_score, insert_student, insert_teacher, sel_info, sel_score
TARGET name_id
FROM log_train
WITH classifier="logistic_regression", batch_size=5;
-- 查看数据库中所有模型
select modelname, createtime, processedtuples,iterations,modeltype, outputtype
from gs_model_warehouse limit 20;
-- 检查模型log_m1的详细参数信息
SELECT gs_explain_model('log_m1');
-- 3. 模型预测
SELECT id,
PREDICT BY log_m1
(FEATURES login, sys, db, insert_score, insert_student, insert_teacher, sel_info, sel_score)
as "PREDICT",
name_id as "LABEL"
INTO temp_pred_train
FROM log_train;
SELECT id,
PREDICT BY log_m1
(FEATURES login, sys, db, insert_score, insert_student, insert_teacher, sel_info, sel_score)
as "PREDICT",
name_id as "LABEL"
INTO temp_pred_test
FROM log_test;
-- 4. 模型评估
-- 计算分类结果的准确率
SELECT
COUNT(*) AS total_count,
SUM(CASE WHEN "PREDICT"= "LABEL" THEN 1 ELSE 0 END) AS correct_count,
CASE
WHEN COUNT(*) = 0 THEN 0.0
ELSE (SUM(CASE WHEN "PREDICT" = "LABEL" THEN 1 ELSE 0 END)::FLOAT / COUNT(*)) * 100.0
END AS accuracy
FROM temp_pred_train;
-- 5. 模型的应用
-- 5.1 用户画像
-- 选择各特征下四分位值作为比较的阈值
CREATE VIEW pm.log_25 AS
SELECT
CASE WHEN PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY login) = 0 THEN 1 ELSE PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY login) END AS login_25,
CASE WHEN PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY sys) = 0 THEN 1 ELSE PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY sys) END AS sys_25,
CASE WHEN PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY db) = 0 THEN 1 ELSE PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY db) END AS db_25,
CASE WHEN PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY insert_all) = 0 THEN 1 ELSE PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY insert_all) END AS insert_all_25,
CASE WHEN PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY insert_score) = 0 THEN 1 ELSE PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY insert_score) END AS insert_score_25,
CASE WHEN PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY sel_info) = 0 THEN 1 ELSE PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY sel_info) END AS sel_info_25,
CASE WHEN PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY sel_score) = 0 THEN 1 ELSE PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY sel_score) END AS sel_score_25
FROM
pm.log_refined;
-- 比较各个样本,得用户标签
SELECT
date,
username,
array_remove(ARRAY[
CASE WHEN login > login_25 THEN '活跃' ELSE NULL END,
CASE WHEN sys > sys_25 THEN '系统维护员' ELSE NULL END,
CASE WHEN db > db_25 THEN '数据库维护员' ELSE NULL END,
CASE WHEN insert_all > insert_all_25 THEN '维护数据表' ELSE NULL END,
CASE WHEN insert_score > insert_score_25 THEN '维护成绩表' ELSE NULL END,
CASE WHEN sel_info > sel_info_25 THEN '查询多' ELSE NULL END,
CASE WHEN sel_score > sel_score_25 THEN '关注成绩' ELSE NULL END
], NULL) AS labels
FROM
log_refined, (SELECT * FROM log_25 LIMIT 1) AS quartiles;
-- 5.2 识别危险用户
SELECT id, username, date,
PREDICT BY log_m1 (FEATURES login, sys, db, insert_score, insert_student, insert_teacher, sel_info, sel_score)
as "PREDICT",
name_id as "LABEL"
FROM log_test2 where "PREDICT"!="LABEL";

166477
UserPortrait/data/audit_log.csv Normal file

File diff suppressed because it is too large Load Diff

2502
UserPortrait/data/log_01.csv Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
id,name_id,date,username,login,sys,db,insert_all,insert_score,sel_info,sel_score,insert_student,insert_teacher
1,2,2023-07-10,pm,7,36,0,0,0,0,0,0,0
2,3,2023-07-10,s_coder,2,0,0,0,0,199,0,0,0
3,4,2023-07-10,t_coder,1,0,0,0,0,26,0,0,0
4,2,2023-07-11,pm,9,16,0,0,0,0,0,0,0
5,3,2023-07-11,s_coder,2,0,0,0,0,211,0,0,0
6,4,2023-07-11,t_coder,1,0,0,0,0,18,0,0,0
7,2,2023-07-12,pm,6,29,0,0,0,0,0,0,0
8,3,2023-07-12,s_coder,2,0,0,0,0,191,0,0,0
9,4,2023-07-12,t_coder,1,0,0,0,0,32,0,0,0
1 id name_id date username login sys db insert_all insert_score sel_info sel_score insert_student insert_teacher
2 1 2 2023-07-10 pm 7 36 0 0 0 0 0 0 0
3 2 3 2023-07-10 s_coder 2 0 0 0 0 199 0 0 0
4 3 4 2023-07-10 t_coder 1 0 0 0 0 26 0 0 0
5 4 2 2023-07-11 pm 9 16 0 0 0 0 0 0 0
6 5 3 2023-07-11 s_coder 2 0 0 0 0 211 0 0 0
7 6 4 2023-07-11 t_coder 1 0 0 0 0 18 0 0 0
8 7 2 2023-07-12 pm 6 29 0 0 0 0 0 0 0
9 8 3 2023-07-12 s_coder 2 0 0 0 0 191 0 0 0
10 9 4 2023-07-12 t_coder 1 0 0 0 0 32 0 0 0

View File

@ -0,0 +1,7 @@
id,name_id,date,username,login,sys,db,insert_all,insert_score,insert_student,insert_teacher,sel_info,sel_score
1,2,2023-07-13,pm,7,36,0,0,0,0,0,0,0
2,3,2023-07-13,s_coder,2,0,0,0,0,0,0,199,0
3,4,2023-07-13,t_coder,1,0,0,0,0,0,0,26,0
4,2,2023-07-14,pm,9,16,0,3,0,0,0,0,0
5,3,2023-07-14,s_coder,2,0,0,0,2,0,0,211,0
6,4,2023-07-14,t_coder,1,0,0,0,0,0,0,18,0
1 id name_id date username login sys db insert_all insert_score insert_student insert_teacher sel_info sel_score
2 1 2 2023-07-13 pm 7 36 0 0 0 0 0 0 0
3 2 3 2023-07-13 s_coder 2 0 0 0 0 0 0 199 0
4 3 4 2023-07-13 t_coder 1 0 0 0 0 0 0 26 0
5 4 2 2023-07-14 pm 9 16 0 3 0 0 0 0 0
6 5 3 2023-07-14 s_coder 2 0 0 0 2 0 0 211 0
7 6 4 2023-07-14 t_coder 1 0 0 0 0 0 0 18 0

View File

@ -0,0 +1,21 @@
id,date,username,name_id,login,sys,db,insert_all,insert_score,insert_student,insert_teacher,sel_info,sel_score
1,2023-07-05,affair,1,3,0,0,3,0,1,1,5,0
2,2023-07-05,pm,2,8,34,8,0,0,0,0,1,0
3,2023-07-05,s_coder,3,2,0,0,0,0,0,0,152,0
4,2023-07-05,t_coder,4,1,0,0,0,0,0,0,18,0
5,2023-07-06,pm,2,7,30,0,0,0,0,0,0,4
6,2023-07-06,s_coder,3,3,0,0,0,0,0,0,145,40
7,2023-07-06,t_coder,4,4,0,0,210,210,0,0,413,5
8,2023-07-07,pm,2,6,29,0,0,0,0,0,0,0
9,2023-07-07,s_coder,3,2,0,0,0,0,0,0,203,0
10,2023-07-07,t_coder,4,1,0,0,0,0,0,0,28,0
11,2023-07-08,pm,2,10,43,0,0,0,0,0,0,0
12,2023-07-08,s_coder,3,2,0,0,0,0,0,0,195,0
13,2023-07-08,t_coder,4,1,0,0,0,0,0,0,21,0
14,2023-07-09,pm,2,5,22,0,0,0,0,0,0,0
15,2023-07-09,s_coder,3,2,0,0,0,0,0,0,186,0
16,2023-07-09,t_coder,4,1,0,0,0,0,0,0,24,0
17,2023-07-06,affair,1,1,0,0,1,0,1,0,0,0
18,2023-07-07,affair,1,1,0,0,2,0,1,1,2,0
19,2023-07-08,affair,1,2,0,0,2,0,0,2,0,0
20,2023-07-09,affair,1,3,0,0,3,0,0,1,2,0
1 id date username name_id login sys db insert_all insert_score insert_student insert_teacher sel_info sel_score
2 1 2023-07-05 affair 1 3 0 0 3 0 1 1 5 0
3 2 2023-07-05 pm 2 8 34 8 0 0 0 0 1 0
4 3 2023-07-05 s_coder 3 2 0 0 0 0 0 0 152 0
5 4 2023-07-05 t_coder 4 1 0 0 0 0 0 0 18 0
6 5 2023-07-06 pm 2 7 30 0 0 0 0 0 0 4
7 6 2023-07-06 s_coder 3 3 0 0 0 0 0 0 145 40
8 7 2023-07-06 t_coder 4 4 0 0 210 210 0 0 413 5
9 8 2023-07-07 pm 2 6 29 0 0 0 0 0 0 0
10 9 2023-07-07 s_coder 3 2 0 0 0 0 0 0 203 0
11 10 2023-07-07 t_coder 4 1 0 0 0 0 0 0 28 0
12 11 2023-07-08 pm 2 10 43 0 0 0 0 0 0 0
13 12 2023-07-08 s_coder 3 2 0 0 0 0 0 0 195 0
14 13 2023-07-08 t_coder 4 1 0 0 0 0 0 0 21 0
15 14 2023-07-09 pm 2 5 22 0 0 0 0 0 0 0
16 15 2023-07-09 s_coder 3 2 0 0 0 0 0 0 186 0
17 16 2023-07-09 t_coder 4 1 0 0 0 0 0 0 24 0
18 17 2023-07-06 affair 1 1 0 0 1 0 1 0 0 0
19 18 2023-07-07 affair 1 1 0 0 2 0 1 1 2 0
20 19 2023-07-08 affair 1 2 0 0 2 0 0 2 0 0
21 20 2023-07-09 affair 1 3 0 0 3 0 0 1 2 0

View File

@ -0,0 +1,160 @@
# openGauss中的AI模块调研
*Tang Wuguo, tangwg@csu.edu.cn*
这篇博客中对openGauss中的AI模块进行介绍并且以iris鸢尾花数据集为例介绍AI模块的使用.
openGauss将AI与数据库结合其中的AI特性大致可分为AI4DB和DB4AI两个部分:
- **AI4DB**就是指用人工智能技术优化数据库的性能,从而获得更好地执行表现;也可以通过人工智能的手段实现自治、免运维等。主要包括自调优、自诊断、自安全、自运维、自愈等子领域;
- **DB4AI**就是指打通数据库到人工智能应用的端到端流程通过数据库来驱动AI任务统一人工智能技术栈达到开箱即用、高性能、节约成本等目的。例如通过SQL-like语句实现推荐系统、图像检索、时序预测等功能充分发挥数据库的高并行、列存储等优势既可以避免数据和碎片化存储的代价又可以避免因信息泄漏造成的安全风险
## DB4AI概要
在我们的用户操作模型的构建中主要使用DB4AI模块借助其中的类似SQL语句的形式直接在openGauss数据库中对数据进行建模、训练及预测。
![DB4AI中的关键字](https://cdn.nlark.com/yuque/0/2023/png/21528568/1688912742558-8b17a5b9-7835-4494-9a26-98e102e82bf6.png#averageHue=%23a9baa8&clientId=ua651ef91-cbbb-4&from=paste&height=398&id=ua8d13ce8&originHeight=497&originWidth=956&originalType=binary&ratio=1.25&rotation=0&showTitle=true&size=33658&status=done&style=none&taskId=ubfbc2031-bc2f-4586-b0e7-21bd33e12bd&title=DB4AI%E4%B8%AD%E7%9A%84%E5%85%B3%E9%94%AE%E5%AD%97&width=764.8 "DB4AI中的关键字")
![DB4AI支持的算法](https://cdn.nlark.com/yuque/0/2023/png/21528568/1688913278733-f973add3-8df8-48f5-bcac-f34e01b79085.png#averageHue=%23eaeacd&clientId=ua651ef91-cbbb-4&from=paste&height=594&id=uf6558da4&originHeight=743&originWidth=930&originalType=binary&ratio=1.25&rotation=0&showTitle=true&size=41287&status=done&style=none&taskId=u5538221a-4ea2-4e48-9f5a-fa7db6b3f45&title=DB4AI%E6%94%AF%E6%8C%81%E7%9A%84%E7%AE%97%E6%B3%95&width=744 "DB4AI支持的算法")
![算法对应的超参数](https://cdn.nlark.com/yuque/0/2023/png/21528568/1688913513261-294838fc-164e-4ff4-8158-1e4e601cbfa1.png#averageHue=%23efefd0&clientId=ua651ef91-cbbb-4&from=paste&height=545&id=ud2629f78&originHeight=681&originWidth=915&originalType=binary&ratio=1.25&rotation=0&showTitle=true&size=119740&status=done&style=none&taskId=ua0478dd5-804b-422f-8fb7-bdd6758d875&title=%E7%AE%97%E6%B3%95%E5%AF%B9%E5%BA%94%E7%9A%84%E8%B6%85%E5%8F%82%E6%95%B0&width=732 "算法对应的超参数")
## 鸢尾花分类模型实践
下面我们已鸢尾花数据集为例使用openGauss中的DB4AI模块搭建两种模型二分类模型多分类模型。同时还利用内置的函数`_gs_explain_model_`查看模型的详细信息,`_PREDICT BY_`关键字来进行模型的推理。
### Iris数据集
iris数据集是ML中最经典的数据集之一其中共包括150个样本对于每个样本有花萼长度、花萼宽度、花瓣长度、花瓣宽度4个特征我们需要依据这4个特征区分3种花型山鸢尾、变色鸢尾还是维吉尼亚鸢尾。
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1689905148831-3470aab1-3871-46de-b762-313ec710f821.png#averageHue=%23050403&clientId=u1918a2c5-2fd8-4&from=paste&height=359&id=ub78bca1f&originHeight=359&originWidth=895&originalType=binary&ratio=1&rotation=0&showTitle=false&size=19999&status=done&style=none&taskId=udfdfd0ed-c801-4b51-bc69-9970db879a1&title=&width=895)
### 划分数据集
我们对iris数据集随机打乱选择80%的数据作为训练集,剩下的作为测试集。
训练集用于模型的训练,模型训练好后可以分别在计算训练集和测试集上的准确率。
```sql
CREATE VIEW iris_random AS
SELECT *,
ROW_NUMBER() OVER () AS row_num,
COUNT(*) OVER () AS total_rows
FROM iris_1
ORDER BY RANDOM();
CREATE VIEW iris_train AS
SELECT *
FROM iris_random
WHERE row_num <= total_rows * 0.8;
CREATE VIEW iris_test AS
SELECT *
FROM iris_random
WHERE row_num > total_rows * 0.8;
```
### 二分类模型
假定我们现在的目标是要区分是否为山鸢尾yes or no这是一个二分类问题这里我们选择使用逻辑回归来解决。
**CREATE MODEL**
```sql
CREATE MODEL iris_m1 USING logistic_regression
FEATURES sepal_length, sepal_width,petal_length,petal_width
TARGET target_id < 2
FROM iris_train
WITH batch_size=20;
```
执行上面的脚本后正常会输出:`MODEL CREATED. PROCESSED 1`
表示模型构建好了,下面测试模型的预测效果
通过查询`gs_model_warehouse`表可以看到数据库中的所有模型
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1689908710289-e0e5598d-1663-41b5-b548-30d7634e82e7.png#averageHue=%230d0a06&clientId=u1918a2c5-2fd8-4&from=paste&height=295&id=ue1c9ecea&originHeight=295&originWidth=1351&originalType=binary&ratio=1&rotation=0&showTitle=false&size=34756&status=done&style=none&taskId=uda9291a2-b250-4773-8739-b50fea9be9f&title=&width=1351)
通过使用`gs_explain_model`函数可以查看指定模型的详细参数:
`select gs_explain_model('iris_m1');`
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1689908757667-b92f9a0f-d725-409a-90ae-bb4db56a779b.png#averageHue=%23020201&clientId=u1918a2c5-2fd8-4&from=paste&height=625&id=ud46d24ff&originHeight=625&originWidth=1706&originalType=binary&ratio=1&rotation=0&showTitle=false&size=31306&status=done&style=none&taskId=u1a8174e3-9c8f-41b9-af61-b5e3906bbda&title=&width=1706)
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1689908832575-392f5e6e-c01f-44a5-a688-27c4bc363131.png#averageHue=%23040301&clientId=u1918a2c5-2fd8-4&from=paste&height=447&id=uced53a58&originHeight=447&originWidth=1084&originalType=binary&ratio=1&rotation=0&showTitle=false&size=16641&status=done&style=none&taskId=ub641b4e5-7a7c-406a-a3e4-4ae393f7b6d&title=&width=1084)
**PREDICT BY**
```sql
SELECT id,
PREDICT BY iris_m1 (FEATURES sepal_length,sepal_width,petal_length,petal_width) as "PREDICT",
target_id < 2 as "LABEL"
FROM iris_train limit 20;
```
```sql
SELECT id,
PREDICT BY iris_m1 (FEATURES sepal_length,sepal_width,petal_length,petal_width) as "PREDICT",
target_id < 2 as "LABEL"
FROM iris_test;
```
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1689908938404-268daf45-26ef-40f0-8494-d7c8b73ca7e6.png#averageHue=%23010101&clientId=u1918a2c5-2fd8-4&from=paste&height=525&id=u863f6f89&originHeight=525&originWidth=1672&originalType=binary&ratio=1&rotation=0&showTitle=false&size=21268&status=done&style=none&taskId=ub1d91d50-8181-444e-a495-bd86b1b5776&title=&width=1672)
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1689910200441-04103459-4afb-4908-ab3e-a6c5fe19840d.png#averageHue=%23020201&clientId=u1918a2c5-2fd8-4&from=paste&height=654&id=u9332e713&originHeight=654&originWidth=964&originalType=binary&ratio=1&rotation=0&showTitle=false&size=24319&status=done&style=none&taskId=u537e2315-adb6-4ffb-bbd7-aae94f1d4e7&title=&width=964)
**计算分类准确率**
准备率 = 分类正确的数量 / 样本总数
```sql
SELECT id,
PREDICT BY iris_m1 (FEATURES sepal_length,sepal_width,petal_length,petal_width) as "PREDICT",
target_id < 2 as "LABEL"
INTO temp_pred
FROM iris_train limit 20;
SELECT
COUNT(*) AS total_count,
SUM(CASE WHEN "PREDICT"= "LABEL" THEN 1 ELSE 0 END) AS correct_count,
CASE
WHEN COUNT(*) = 0 THEN 0.0
ELSE (SUM(CASE WHEN "PREDICT" = "LABEL" THEN 1 ELSE 0 END)::FLOAT / COUNT(*)) * 100.0
END AS accuracy
FROM temp_pred;
```
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1689910258939-bcf185ba-f13c-4dd5-b40e-4a13295a087e.png#averageHue=%23050403&clientId=u1918a2c5-2fd8-4&from=paste&height=97&id=uf92bcd78&originHeight=97&originWidth=1236&originalType=binary&ratio=1&rotation=0&showTitle=false&size=4228&status=done&style=none&taskId=u029530e4-fd79-44af-8f9d-1050b76e77c&title=&width=1236)
### 多分类模型
假定现在的任务是给定一个样本要预测是3种花型中的哪一种这是一个多分类的问题。
```sql
CREATE MODEL iris_m2 USING multiclass
FEATURES sepal_length, sepal_width,petal_length,petal_width
TARGET target_id
FROM iris_train
WITH classifier="logistic_regression", batch_size=20,max_iterations=300,learning_rate = 1.0;
drop table temp_pred;
SELECT id,
PREDICT BY iris_m2 (FEATURES sepal_length,sepal_width,petal_length,petal_width) as "PREDICT",
target_id as "LABEL"
INTO temp_pred
FROM iris_test;
SELECT
COUNT(*) AS total_count,
SUM(CASE WHEN "PREDICT"= "LABEL" THEN 1 ELSE 0 END) AS correct_count,
CASE
WHEN COUNT(*) = 0 THEN 0.0
ELSE (SUM(CASE WHEN "PREDICT" = "LABEL" THEN 1 ELSE 0 END)::FLOAT / COUNT(*)) * 100.0
END AS accuracy
FROM temp_pred;
```
batch_size=20
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1689911528931-4a1c0444-7541-4125-bae1-e0f2631ab1cb.png#averageHue=%23060504&clientId=u1918a2c5-2fd8-4&from=paste&height=69&id=ua51023e9&originHeight=69&originWidth=509&originalType=binary&ratio=1&rotation=0&showTitle=false&size=2084&status=done&style=none&taskId=u91ebd585-9a27-4d86-b118-359bb92b9f4&title=&width=509)
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1689911625210-ad7c89a7-fc76-4f9d-9fc7-2babeabc4b45.png#averageHue=%23040403&clientId=u1918a2c5-2fd8-4&from=paste&height=70&id=u96b5144e&originHeight=70&originWidth=420&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1844&status=done&style=none&taskId=u3280af96-a388-4274-ab16-e767965c57a&title=&width=420)
经过调节超参数batch_size=4在训练集上的结果可以提升
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1689911352977-03b556ed-b321-483b-a771-d1f72e062c6f.png#averageHue=%23040403&clientId=u1918a2c5-2fd8-4&from=paste&height=73&id=ue3370894&originHeight=73&originWidth=430&originalType=binary&ratio=1&rotation=0&showTitle=false&size=2000&status=done&style=none&taskId=ud6f60a09-cfa8-429b-b429-da1cb8f80ee&title=%E8%AE%AD%E7%BB%83%E9%9B%86%E4%B8%8A%E7%BB%93%E6%9E%9C&width=430)
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1689911363647-25e11954-1953-49f2-8519-ecca0a73ae6f.png#averageHue=%23070504&clientId=u1918a2c5-2fd8-4&from=paste&height=65&id=u33bf56ed&originHeight=65&originWidth=491&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1871&status=done&style=none&taskId=u6ee262f6-cdb6-4ed1-a4d0-8ba0e52999f&title=%E6%B5%8B%E8%AF%95%E9%9B%86&width=491)
## 小结
最后总结下使用openGauss中AI模块的流程
1. 处理数据集
1. 数据集中的每个样本通常包括多个属性列和标签列建议添加一个id列用于标识每个样本
2. 划分数据集在ML中需要在训练集上训练模型在测试集上评估
3. 明确属于是哪种任务类型分类or回归才能选择后面相应的算法
2. 创建模型
1. `CREATE MODEL`关键字创建模型
2. `DROP MODEL xxx` 删除模型
3. `SELECT gs_explain_model('xxx')` 查看模型详细信息
4. `SELECT modelname, createtime, processedtuples,iterations,modeltype, outputtype FROM gs_model_warehouse LIMIT 5;`查看数据库中所有模型
3. 模型推理
1. 通产推理结果应该包含样本的id, 预测值PREDICT, 标签值LABEL
2. 使用`PREDICT BY`关键字进行模型的推理得到PREDICT列
4. 模型评估
1. 可以通过SQL语句计算ACC准确率for 分类、MSE均方误差for 回归)等指标
2. 也可以将预测的结果导出到CSV文件使用其他工具来分析`COPY(...)TO '/path' WITH(FORMAT CSV, HEADER)`
5. 模型调参
1. 通过观察模型在训练集上的结果,我们可以通过调整不同的超参数来创建不同的模型
2. 不同的模型有这不同的可调参数,可以参考文档中列出的[这张表](https://docs.opengauss.org/zh/docs/5.0.0/docs/AIFeatureGuide/DB4AI-Query-%E6%A8%A1%E5%9E%8B%E8%AE%AD%E7%BB%83%E5%92%8C%E6%8E%A8%E6%96%AD.html#:~:text=%E8%A1%A8%203-,%E8%B6%85%E5%8F%82%E7%9A%84%E9%BB%98%E8%AE%A4%E5%80%BC%E4%BB%A5%E5%8F%8A%E5%8F%96%E5%80%BC%E8%8C%83%E5%9B%B4,-%E7%AE%97%E5%AD%90)。
3. 当你觉的效果足够满意了,就将这组参数选定作为最终的模型
*参考资料*
[openGauss Doc-Ai特性](https://docs.opengauss.org/zh/docs/5.0.0/docs/AIFeatureGuide/AI%E7%89%B9%E6%80%A7.html)
[openGauss Doc-原生Db4Ai引擎](https://docs.opengauss.org/zh/docs/5.0.0/docs/AIFeatureGuide/%E5%8E%9F%E7%94%9FDB4AI%E5%BC%95%E6%93%8E.html)

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@ -0,0 +1,156 @@
# 审计日志调研
*Tang Wuguo, tangwg@csu.edu.cn*
因为后续工作需要对数据库用户的操作进行建模并进行用户画像第一步就是需要获取到用户操作日志即审计日志。在这篇博客中将介绍openGauss中的审计日志部分的内容包括如何获取审计日志设置审计日志导出审计日志等内容。
## 1.日志位置
查询到各种配置文件的路径:
例如postgresql.conf文件路径`/var/lib/opengauss/data/postgresql.conf`
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1687868951719-99e12dd1-bef9-4450-8ddf-9ff16f346e57.png#averageHue=%23070503&clientId=u93c09203-c893-4&from=paste&height=208&id=cD64M&originHeight=208&originWidth=768&originalType=binary&ratio=1&rotation=0&showTitle=false&size=14234&status=done&style=none&taskId=u6e163485-ae62-4245-a892-333fae562f5&title=&width=768)
查看审计日志的文件夹名:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1687858604714-bedd0ddf-35f3-4968-b86a-0c1320a49535.png#averageHue=%23080808&clientId=u93c09203-c893-4&from=paste&height=103&id=M6FaW&originHeight=103&originWidth=324&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1348&status=done&style=none&taskId=u03c625ec-d12c-43c0-a413-100daa7b306&title=&width=324)
审计日志完整路径:`/var/lib/opengauss/data/pg_audit`
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1687864775610-3f3c3bae-7b6b-4ce6-94d7-c261cc41cb71.png#averageHue=%23f9f7f6&clientId=u93c09203-c893-4&from=paste&height=227&id=JA3Ic&originHeight=227&originWidth=842&originalType=binary&ratio=1&rotation=0&showTitle=false&size=21903&status=done&style=none&taskId=u01767339-37fe-4aef-8afd-82d432f508a&title=&width=842)
## 2.比较OG和PG 中的日志管理
**openGauss**
- pg_log
数据库运行日志openGauss运行时数据库节点以及openGauss安装部署时产生的日志统称为系统日日志
- pg_xlog
WAL 日志,事务日志信息即重做日志
- pg_clog
事务提交日志,记录的是事务的元数据
- pg_audit
审计功能开启时会不断产生大量的审计日志,占用磁盘空间。用户可以根据磁盘空间的大小设置审计日志维护策略。
- gs_profile
性能日志指的是数据库系统在运行时检测物理资源的运行状态的日志,在对外部资源进行访问时的性能检测,包括磁盘等外部资源的访问检测信息。
**postgresql**
- pg_log
- pg_xlog
- pg_clog
## 3.审计日志
openGauss将用户对数据库的所有操作写入审计日志。数据库安全管理员可以利用这些日志信息重现导致数据库现状的一系列事件找出非法操作的用户、时间和内容等。
默认时开启审计功能的[audit_enabled](https://docs.opengauss.org/zh/docs/5.0.0/docs/DatabaseReference//%E5%AE%A1%E8%AE%A1%E5%BC%80%E5%85%B3.html#zh-cn_topic_0237124745_zh-cn_topic_0059777744_sb3d1b703f24c49f096b36087a60fdfcd),除了审计总开关,**各个审计项**也有对应的开关。只有开关开启,对应的审计功能才能生效。
### 3.1审计相关的配置
下面列出了所有审计配置及解释,其中就包括对各个审计项的配置:
```bash
audit_enabled = on
audit_directory = 'pg_audit' # 审计文件的存储目录
audit_data_format = 'binary' # 审计日志文件的格式当前仅支持二进制格式binary
audit_rotation_interval = 1d # 创建一个新审计日志文件的时间间隔default 1 day
audit_rotation_size = 10MB # 审计日志文件的最大容量, 超过此参数值时将生成一个新日志
audit_space_limit = 1024MB # 审计文件占用磁盘空间的最大值
audit_file_remain_threshold = 1048576 # 审计目录下审计文件的最大数量
# --------具体的审计项--------
# 1, 7 表示开启0表示关闭
#---用户和权限审计
audit_login_logout = 7 # 用户登录、退出的审计
audit_database_process = 1 # 数据库启动、停止、恢复和切换的审计
audit_user_locked = 1 # 用户锁定和解锁
audit_user_violation = 0 # 用户越权操作审计
audit_grant_revoke = 1 # 用户权限授予和回收审计
#---各类操作审计
# 数据库对象的CREATEALTERDROP操作审计,
# 由29个二进制位数表示分别代表了26类数据库对象
# 12295表示审计DATABASE, SCHEMA, USER, DATA SOURCE
audit_system_object = 12295
# 具体表的INSERT、UPDATE和DELETE操作审计, 默认关闭
# 注意不包括SELECT
audit_dml_state = 0
# 是否对SELECT操作进行审计
audit_dml_state_select = 0
# 在执行存储过程、匿名块或自定义函数(不包括系统自带函数)时是否记录审计信息
audit_function_exec = 0
# 是否对COPY操作进行审计
audit_copy_exec = 0
# 是否对SET操作进行审计
audit_set_parameter = 1
```
### 3.2配置审计项
这里我们在默认值的基础上打开对表操作的审计执行存储过程、函数的审计copy操作的审计
```bash
# 12295表示审计DATABASE, SCHEMA, USER, DATA SOURCE
# 12303表示打开第4位的审计即对表格创建审计
audit_system_object = 12303
# 具体表的INSERT、UPDATE和DELETE操作审计
audit_dml_state = 0
# 是否对SELECT操作进行审计
audit_dml_state_select = 0
# 在执行存储过程、匿名块或自定义函数(不包括系统自带函数)时是否记录审计信息
audit_function_exec = 0
# 是否对COPY操作进行审计
audit_copy_exec = 0
```
```bash
# 检查audit_dml_state配置值
cat /var/lib/opengauss/data/postgresql.conf | grep audit_dml_state
# 修改配置
gs_guc set -D /var/lib/opengauss/data -c "audit_dml_state=1"
gs_guc set -D /var/lib/opengauss/data -c "audit_dml_state_select=1"
gs_guc set -D /var/lib/opengauss/data -c "audit_function_exec=1"
gs_guc set -D /var/lib/opengauss/data -c "audit_copy_exec=1"
gs_guc set -D /var/lib/opengauss/data -c "audit_system_object=12303"
# 重启数据库使参数生效
gs_ctl restart -D /var/lib/opengauss/data
```
我们也可以直接修改postgresql.conf文件来配置修改后也需要重启数据库。
### 3.3查询审计日志
通过下面的查询语句,可以查询到指定时间段内的所有审计日志。
```sql
select * from pg_query_audit('2023-05-18 00:00:00','2025-05-18 08:00:00');
```
```sql
time | type | result | userid | username | database | client_conninfo | object_name | detail_info | node_name | thread_id | local_port | remote_port
------------------------+----------------+--------+--------+-----------+-----------+-------------------------+-------------------+--------------+-------------------+---------------------------------+------------+-------------
2021-03-04 08:00:08+08 | login_success | ok | 10 | omm | postgres | gsql@::1 | postgres | login db(postgres) success, SSL=off | dn_6001_6002_6003 | 140477687527168@668131208211425 |17778 | 46946
```
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1687913714166-d19bf778-e202-40cd-b9f3-01836e1758a2.png#averageHue=%23060503&clientId=ua93674ac-9042-4&from=paste&height=671&id=ube647c66&originHeight=671&originWidth=1699&originalType=binary&ratio=1&rotation=0&showTitle=false&size=82421&status=done&style=none&taskId=u694ca101-ad34-47ee-9cd1-1853f2eb591&title=&width=1699)
### 3.4审计日志存储到表格
在前面我们可以通过查询语句查看日志,但是仍不方便。我们可以使用下面的语句将查询到的日志信息存储到表格中,方便我们观察、过滤日志信息。
```sql
CREATE TABLE audit_log
(
log_time timestamp(3) with time zone,
type text,
result text,
userid integer,
username text,
database text,
client_conninfo text,
object_name text,
detail_info text,
node_name text,
thread_id text,
local_port text,
remote_port text,
);
SELECT * INTO audit_log FROM pg_query_audit('2023-05-18 00:00:00','2025-05-18 08:00:00');
```
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1688043130992-5b7c70ea-b918-4137-bbdf-26a6b8275297.png#averageHue=%23e5ebb5&clientId=u50dfbd15-fe5e-4&from=paste&height=827&id=u0abd4cb0&originHeight=827&originWidth=1197&originalType=binary&ratio=1&rotation=0&showTitle=false&size=120759&status=done&style=stroke&taskId=u906a0ed9-51c7-4409-af5f-1a76618f8d7&title=&width=1197)
*参考资料*
[PostgreSQL日志管理-CSDN博客](https://blog.csdn.net/weixin_41287260/article/details/120471931)
[openGauss Doc-审计概述](https://docs.opengauss.org/zh/docs/5.0.0/docs/DatabaseAdministrationGuide/%E5%AE%A1%E8%AE%A1%E6%A6%82%E8%BF%B0.html)
[openGauss Doc-日志参考](https://docs.opengauss.org/zh/docs/5.0.0/docs/DatabaseOMGuide/%E6%97%A5%E5%BF%97%E5%8F%82%E8%80%83.html)

View File

@ -0,0 +1,82 @@
# 设计文档
*Tang Wuguo, tangwg@csu.edu.cn*
首先我们明确一下将用户的画像问题看作一个多分类任务确定好模型的输入输出。模型的输入是设计好的用户操作特征模型输出的是该用户的类型。而要从原始的审计日志到用户特征需要经过特征工程模块在用户操作模型中选用的是openGauss中定义好的机器学习模型。![用户操作模型](https://cdn.nlark.com/yuque/0/2023/png/21528568/1689931339917-568c1737-b9d2-4e40-b013-776b846ec967.png#averageHue=%23f9f7f4&clientId=u862145d8-5212-4&from=paste&id=uc944a0f2&originHeight=302&originWidth=1217&originalType=url&ratio=1&rotation=0&showTitle=true&size=44504&status=done&style=none&taskId=u1ea3bcff-0c69-4d9d-91db-57306164042&title=%E7%94%A8%E6%88%B7%E6%93%8D%E4%BD%9C%E6%A8%A1%E5%9E%8B "用户操作模型")
整体的流程如下图所示:
1. 首先需要收集指定时间段的审计审计日志
2. 通过分析改日志来筛除一些无效的数据,设计一些用户特征,例如用户的一次操作需要包括主体、客体、操作类型(哪个用户对谁做了什么)等信息;
3. 接着需要处理数据集,例如给类别数据编号,划分数据集;
4. 使用为了评估模型分类的好坏,这里选择准确率作为指标;
5. 根据指标的好坏,我们可以调整模型的超参数,使得模型在训练集上分类得更好;
6. 选定好超参数后模型就可以部署上线,之后数据库中又会产生许多的用户操作日志,我们可以重新收集日志来迭代模型;
![用户画像任务执行流程](https://cdn.nlark.com/yuque/0/2023/png/21528568/1689930304365-e9e46716-bd55-4c3c-aa54-8af83ed6a634.png#averageHue=%23f8f8f8&clientId=u862145d8-5212-4&from=paste&id=uf685c4ef&originHeight=812&originWidth=1005&originalType=url&ratio=1&rotation=0&showTitle=true&size=73439&status=done&style=none&taskId=u79639d8f-757c-4152-935f-d420127696a&title=%E7%94%A8%E6%88%B7%E7%94%BB%E5%83%8F%E4%BB%BB%E5%8A%A1%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B "用户画像任务执行流程")
## 1.模拟审计日志
由于没有找到公开的审计日志可以使用于是设计了一个多数据库用户操作的教务管理系统并使用python脚本模拟各个用户操作数据库从而得到审计日志。
模拟日志流程:
![image.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1688373361560-18f787c6-db66-4fe5-bc3d-15c122d2e0f0.png#averageHue=%23f5f5f5&clientId=uc9cc8d10-48e3-4&from=paste&id=u09112984&originHeight=1062&originWidth=1504&originalType=url&ratio=1&rotation=0&showTitle=false&size=169531&status=done&style=none&taskId=u0fc696c9-78c4-47c1-95f1-d096e3f10c6&title=)
### 1.1数据库db_school设计
该数据库包括两个模式教务管理系统中的各种数据存在public模式下操作日志、操作模型相关的数据都存在pm模式下。
这个教务管理系统中由4个表组成
教师表(工号,姓名)
学生表(学号,姓名,班级)
课程表(课程号,授课老师工号,课程名,学分)
成绩表(学号,课程号,成绩)
![数据库设计_用户操作模型.drawio.png](https://cdn.nlark.com/yuque/0/2023/png/21528568/1688179079909-62ff4099-5583-4b60-a785-1ff39dc1c060.png#averageHue=%23f9f9f9&clientId=ue9f5a0b9-ad20-4&from=paste&height=1022&id=u3bbe4e2d&originHeight=1022&originWidth=1502&originalType=binary&ratio=1&rotation=0&showTitle=false&size=115219&status=done&style=none&taskId=u53db3b75-75f7-40ef-8519-4068c15efa7&title=&width=1502)
![DBeaver中生成的ER图](https://cdn.nlark.com/yuque/0/2023/png/21528568/1688027613619-80928869-94e8-4197-b4af-ace2afdba452.png#averageHue=%23fbfafa&clientId=u50dfbd15-fe5e-4&from=paste&height=474&id=kUA7E&originHeight=535&originWidth=578&originalType=binary&ratio=1&rotation=0&showTitle=true&size=21126&status=done&style=stroke&taskId=ud81e2879-7c2d-49b6-8e2b-86c26d7d025&title=DBeaver%E4%B8%AD%E7%94%9F%E6%88%90%E7%9A%84ER%E5%9B%BE&width=512 "DBeaver中生成的ER图")
### 1.2多用户设计
opengauss超级管理员创建用户pm
- pm项目架构师负责设计、创建数据库、表格、视图创建下面的用户
- t_coder老师端主要对成绩表增、改
- s_coder学生端成绩查询
- affair教务处负责增删学生表、老师表、课程表
### 1.3各个用户的权限设计
openGauss支持以下的权限SELECT、INSERT、UPDATE、DELETE、TRUNCATE、REFERENCES、CREATE、CONNECT、EXECUTE、USAGE、ALTER、DROP、COMMENT、INDEX和VACUUM。不同的权限与不同的对象类型关联对象所有者的权限是隐式拥有的即只要拥有对象就可以执行对象所有者的这些隐式权限。
**pm**: sysadmin
**s_coder**: SELECT ON ALL TABLES IN SCHEMA public
**t_coder**: SELECT ON ALL TABLES IN SCHEMA public, UPDATE ON public.score
**affair**: SELECT ON ALL TABLES IN SCHEMA public, UPDATE ON ALL TABLES IN SCHEMA public
### 1.4模拟用户操作
准备40名学生、5名老师、5门课程信息使用python脚本模拟5天操作的数据
具体是使用`psycopg2`库创建数据库连接对象,再使用数据库连接对象执行增删改查操作。
## 2.用户操作模型
### 2.1日志中的用户操作类型
通过观察日志可以发现操作主要分为:
- 系统级别的操作,如登录,创建用户,分配权限,设置参数
- 数据库级操作,创建数据库,模式,表
- 表级操作,对表的增删改查
因此,在设计操作特征的时候,也按照这些类别创建了各类操作的视图(所有视图均是统计每天的次数):
- query_login用户每天的登录次数
- query_sys除了登录操作其他的系统级操作次数
- query_db数据库级的操作次数
- query_insert_all所有对表的插入操作
- query_insert_score所有对score表的插入操作
- query_insert_score所有对score表的插入操作
- query_sel_info各类信息表的查询
- query_sel_score对成绩表的查询
### 2.2模型应用场景
**给某日用户的画像**
首先通过计算训练数据中各个特征的下四分位值a_25作为我们的判定阈值。即有75%的数据是大于这个a_25a_25排在后25名的位置同时防止该特征出现的很少例如操作数据库的特征绝大部分数据都会等于0此时对于这种情况我们手动设置该阈值为1。
**识别危险用户操作**
根据用户的操作行为,判断其是否行为异常。即该用户的操作特征与其实际用户标签不符。
在测试时故意增加了一条s_coder的异常数据例如该一条学生客户端端的数据的成绩表修改次数改为正值但其实学生端是没法对成绩表进行增改的看模型是否能预测出的异常行为。