0%

SQL Server(version 2005)读取数据库中的表信息

1
2
3
4
5
6
select
name as 'table_name',
create_date AS 'create_time',
modify_date AS 'update_time'
from sys.tables
where type = 'U'

SQL Server(version 2005)读取表中的列信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
SELECT
a.name as 'column_name',
b.name as 'data_type',
COLUMNPROPERTY(a.id,a.name,'PRECISION') as 'data_length',
COLUMNPROPERTY(a.id,a.name,'Scale') as 'data_scale',
case when exists(SELECT 1 FROM sysobjects where xtype='PK' and parent_obj=a.id and name in (
SELECT name FROM sysindexes WHERE indid in( SELECT indid FROM sysindexkeys WHERE id = a.id AND colid=a.colid))) then 1 else 0 end as 'primary_key',
COLUMNPROPERTY(a.id,a.name,'IsIdentity') as 'autoincrement',
a.isnullable as 'nullable',
e.text as 'column_default',
g.value as 'column_comment'
FROM syscolumns a
left join systypes b on a.xusertype=b.xusertype
left join sysobjects d on a.id=d.id and d.xtype='U'
left join syscomments e on a.cdefault=e.id
left join sys.extended_properties g on a.id=g.major_id and a.colid=g.minor_id
where d.name='Role'
order by a.id, a.colorder

查询结果示例如下:

column_name data_type data_length data_scale primary_key autoincrement nullable column_default column_comment
id int 10 0 1 1 0
name nvarchar 50 0 0 0 名称
description nvarchar 300 0 0 1 描述

MySQL(version 5.7)读取数据库中的表信息

1
2
3
4
5
6
7
8
SELECT
TABLE_NAME AS 'table_name',
TABLE_ROWS AS 'table_rows',
CREATE_TIME AS 'create_time',
UPDATE_TIME AS 'update_time'
FROM
information_schema.TABLES
WHERE TABLE_SCHEMA ='test'

其中TABLE_SCHEMA是数据库名。
查询结果示例如下:

table_name table_rows create_time update_time
goods 5 2020/1/15 17:10 2020/1/15 17:10

其中update_time是表结构的更新时间,而不是表数据的更新时间,而且我测试下来create_time也会跟着变化不知为何。

MySQL(version 5.7)读取表中的列信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT
COLUMN_NAME as 'column_name',
DATA_TYPE as 'data_type',
IFNULL(CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION) as 'data_length',
NUMERIC_SCALE as 'data_scale',
COLUMN_TYPE as 'column_type',
IF(COLUMN_KEY='PRI', 1, 0) as 'primary_key',
IF(EXTRA='auto_increment', 1, 0) as 'autoincrement',
IF(IS_NULLABLE='YES', 1, 0) as 'nullable',
COLUMN_DEFAULT as 'column_default',
COLUMN_COMMENT as 'column_comment'
FROM
information_schema.COLUMNS
WHERE TABLE_SCHEMA ='test' and TABLE_NAME = 'goods'

其中TABLE_SCHEMA是数据库名,TABLE_NAME是表名。
查询结果示例如下:

column_name data_type data_length data_scale column_type primary_key autoincrement nullable column_default column_comment
id int 10 0 int(11) 1 1 0
name varchar 100 varchar(100) 0 0 1 名称
price decimal 18 2 decimal(18,2) 0 0 0 0 价格

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
# -*- coding: utf-8 -*-
from redis import Redis
import threading
import time
import uuid

# 互斥锁
class Mutex:
def __init__(self, name, server="127.0.0.1"):
self.name = name
self.key_name = "MUTEX_" + name
self.id = uuid.uuid4().hex
self.redis = Redis(host=server)

def acquire(self, blocking=True, ex=120):
r = self.redis.set(self.key_name, self.id, ex=ex, nx=True)
if blocking:
while not r:
time.sleep(0.01)
r = self.redis.set(self.key_name, self.id, ex=ex, nx=True)
return r

def release(self):
if self.acquired():
self.redis.delete(self.key_name)

def acquired(self):
r = self.redis.get(self.key_name)
return r != None and r.decode() == str(self.id)

def __enter__(self):
self.acquire()

def __exit__(self, exc_type, exc_value, exc_trackback):
self.release()
if exc_value != None:
raise exc_value


# 读写锁
class ReadWriteLock:
def __init__(self, name, server="127.0.0.1"):
self.name = name
self.server = server
self.rlock_name = "RLOCK_" + name
self.wlock_name = "WLOCK_" + name
self.id = uuid.uuid4().hex
self.redis = Redis(host=server)
self.lock_type = None

def read_lock(self, blocking=True, ex=120):
mutex = Mutex(self.name, self.server)
try:
mutex.acquire()
wlock_locked = self.redis.get(self.wlock_name)
if wlock_locked:
if blocking:
while wlock_locked:
mutex.release()
time.sleep(0.05)
mutex.acquire()
wlock_locked = self.redis.get(self.wlock_name)
else:
return False

pipeline = self.redis.pipeline()
now = time.time()
pipeline.zremrangebyscore(self.rlock_name, 0, int((now-ex)*1000) )
pipeline.zadd(self.rlock_name, {self.id: int(now*1000)} )
pipeline.expire(self.rlock_name, ex)
pipeline.execute()
self.lock_type = 'R'
return True
except Exception as e:
raise
finally:
mutex.release()

def write_lock(self, blocking=True, ex=120):
mutex = Mutex(self.name, self.server)
try:
mutex.acquire()
self.redis.zremrangebyscore(self.rlock_name, 0, int((time.time()-ex)*1000) )
r = self.redis.zcard(self.rlock_name)
if r:
if blocking:
while r:
mutex.release()
time.sleep(0.05)
mutex.acquire()
self.redis.zremrangebyscore(self.rlock_name, 0, int((time.time()-ex)*1000) )
r = self.redis.zcard(self.rlock_name)
else:
return False
r = self.redis.set(self.wlock_name, self.id, ex=ex, nx=True)
if blocking:
while not r:
mutex.release()
time.sleep(0.05)
mutex.acquire()
r = self.redis.set(self.wlock_name, self.id, ex=ex, nx=True)
if r:
self.lock_type = 'W'
return r
except Exception as e:
raise
finally:
mutex.release()

def unlock(self):
if self.lock_type == 'R':
self.redis.zrem(self.rlock_name, self.id)
self.lock_type = None
elif self.lock_type == 'W':
r = self.redis.get(self.wlock_name)
if r != None and r.decode() == str(self.id):
self.redis.delete(self.wlock_name)
self.lock_type = None

支持阻塞和非阻塞加锁,默认阻塞,非阻塞用法。
考虑了锁住后崩溃,解决方案是超时后自动结束。
考虑了未崩溃但是超时(这种情况首先应该调整超时时间设置,或者程序应调整锁住的代码,例如多次分段锁)。
在这种异常情况发生时,可能产生一边释放了锁但还在访问,另一边加上了锁,记住这是异常情况,但我们要保证即使它发生了也尽量能正常工作下去,
对于确实存在访问冲突的那么是没办法的,该异常就异常好了,还有种情况是虽然加了锁,但是并没有访问冲突,其实程序可以正常下去,但是这里会发生什么呢?

1
2
3
4
对于A线程,手动加锁----------超时自动解锁|------------------------手动解锁|
对于B线程,-------------手动加锁-------|(等到此加锁成功)---------------------手动解锁|
对于C线程,-------------------------------|------------手动加锁-------|(如果在此加锁成功是错误的!)

A线程因为超时自动解锁后虽然和B线程没有发生访问冲突,但是它解了B线程的锁,导致C线程加锁成功,而B线程实际还没解锁,这又制造了潜在的B线程和C线程的访问冲突。
所以手动解锁时应该判断下当前的锁是否是自己加的。这就是acquired函数中r.decode() == str(self.id)存在的意义。

封装基类

模型基类,管理engine,事务处理,JSON序列化 的代码

通用代码 db_.py

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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# -*- coding: utf-8 -*-
from sqlalchemy.ext.declarative import declarative_base, declared_attr
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from contextlib import contextmanager
from urllib import parse
import re
import datetime
import types
import decimal


class ModelBase(object):

@declared_attr
def __tablename__(cls):
# return cls.__name__.lower()
return re.sub(r'([A-Z])', r'_\1', cls.__name__[0].lower()+cls.__name__[1:]).lower()

@classmethod
def props(cls):
if cls.__base__.__name__ == "Base":
return [c for c in cls.__table__.columns]
elif cls.__base__.__base__.__name__ == "Base":
super_column_names = [c for c in cls.__base__.__table__.columns]
column_names = [c for c in cls.__table__.columns if c.name != 'id']
return super_column_names + column_names
else:
assert(False and "多层继承后的props获取暂未实现")

@classmethod
def prop_names(cls):
if cls.__base__.__name__ == "Base":
return [c.name for c in cls.__table__.columns]
elif cls.__base__.__base__.__name__ == "Base":
super_column_names = [c.name for c in cls.__base__.__table__.columns]
column_names = [c.name for c in cls.__table__.columns if c.name != 'id']
return super_column_names + column_names
else:
assert(False and "多层继承后的prop_names获取暂未实现")

@classmethod
def prop(cls, prop_name):
return cls.__table__.columns[prop_name]

# 对象在输出时如何序列化成字符串
def __repr__(self):
attrs = []
# for c in self.__table__.columns:
for name in self.prop_names():
attr = getattr(self, name)
if type(attr) in (str, datetime.date, datetime.time, datetime.datetime):
attrs.append(f"{name}='{attr}'")
else:
attrs.append(f"{name}={attr}")
return f"{self.__class__.__name__}({', '.join(attrs)})"

# 转换成dict时的键列表
def keys(self):
return self.prop_names()

def __getitem__(self, item):
return getattr(self, item)

def __setitem__(self, item, value):
return setattr(self, item, value)

def to_dict(self):
return {name: to_jsonable(getattr(self, name)) for name in self.keys()}


def to_dict(db_obj):
if isinstance(db_obj, ModelBase):
return db_obj.to_dict()
else:
return db_obj

def to_list(db_objs):
return [to_dict(db_obj) for db_obj in db_objs]


# Base = declarative_base(cls=ModelBase)


# 把ORM对象转成可序列化成JSON的对象,对于ORM对象的list转换为dict的list,对于ORM对象转换成dict
def to_jsonable(o):
if isinstance(o, list):
return [to_jsonable(e) for e in o]
if isinstance(o, dict):
return {k: to_jsonable(v) for (k,v) in o.items()}
if isinstance(o, ModelBase):
return o.to_dict()
if isinstance(o, datetime.datetime):
return o.strftime('%Y-%m-%d %H:%M:%S')
if isinstance(o, datetime.date):
return o.strftime('%Y-%m-%d')
if isinstance(o, datetime.time):
return o.strftime('%H:%M:%S')
if isinstance(o, decimal.Decimal):
return float(o)
return o


engines = {}

r"""
不同数据库URL样例
r"sqlite:///:memory:" sqlite内存数据库
r"sqlite:///C:\path\foo.db" sqlite在windows上
r"sqlite:////root/data/foo.db" sqlite在linux上
r"mysql+pymysql://username:password@server/dbname" MySQL
r"postgresql+psycopg2://username:password@server/dbname" Postgresql
r"mssql+pymssql://username:password@server/dbname?charset=utf8" Microsoft SQL Server
"""
def init_engine(
url=None, name="main",
dialect=None, username="", password="", server="", dbname=None,
**kwargs):
if dialect:
if dialect.lower() in ("mysql", ):
dialect = "mysql+pymysql"
elif dialect.lower() in ("postgresql", "postgres", "pgsql"):
dialect = "postgresql+psycopg2"
elif dialect.lower() in ("mssql", "sqlserver"):
dialect = "mssql+pymssql"
if url is None:
if password:
url = '{}://{}:{}@{}/{}'.format(dialect, username, parse.quote_plus(password), server, dbname)
elif username:
url = '{}://{}@{}/{}'.format(dialect, username, server, dbname)
else:
url = '{}://{}/{}'.format(dialect, server, dbname)

# # 设置连接池默认大小
# if "pool_size" not in kwargs:
# kwargs["pool_size"] = 5

# # 设置连接默认回收时间(MySQL默认8小时)
# if "pool_recycle" not in kwargs:
# kwargs["pool_recycle"] = 28000

# 默认启用pool_pre_ping
if "pool_pre_ping" not in kwargs:
kwargs["pool_pre_ping"] = True

engine = create_engine(url, **kwargs)
session_maker = sessionmaker(expire_on_commit=False)
session_maker.configure(bind=engine)

@contextmanager
def _session_scope(maker):
session = maker()
mutexes = []
try:
yield session
session.commit()
except Exception:
session.rollback()
raise
finally:
session.close()
engine.session_scope = types.MethodType(_session_scope, session_maker)
engines[name] = engine
return engine


def exit_engine(name="main"):
if name in engines:
del engines[name]


def get_engine(name="main"):
if name in engines:
return engines[name]
else:
return None


def session_scope(name="main"):
if name in engines:
return engines[name].session_scope()
else:
raise Exception("engine未初始化")

数据库模型(表)定义

db_test.py 基于db_.py来定义数据库和使用数据库,包括如何定义关系映射,如何定义外键,唯一键,索引,如何将查询结果转换成可JSON序列化的对象(以便通过RESTful API返回)

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
# -*- coding: utf-8 -*-
from sqlalchemy import Table, Column, Integer, Float, Numeric, String, DateTime, Date, Time, Boolean
from sqlalchemy import PrimaryKeyConstraint, ForeignKey, Index, UniqueConstraint
from sqlalchemy import text, func, and_, or_, not_, asc, inspect, desc, distinct
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declarative_base

from utils.db_ import ModelBase, init_engine, exit_engine, get_engine, session_scope, to_jsonable

Base = declarative_base(cls=ModelBase)


class Clazz(Base):
id = Column(Integer(), primary_key=True, autoincrement=True)
enroll_year = Column(Integer(), comment="入学年份")


class Student(Base):
__table_args__ = (
UniqueConstraint('clazz_id', 'name'), # 创建多字段唯一约束
Index('ix_clazz_id_stu_no', 'clazz_id', 'stu_no'), # 创建多字段索引
)
id = Column(Integer(), primary_key=True, autoincrement=True) # 自增ID作为单字段主键,多字段主键也可以在多字段上都写上primary_key=True
stu_no = Column(String(), index=True, unique=True, comment="学号") # 创建单字段索引,创建单字段唯一约束
name = Column(String(), nullable=True, comment="姓名") # 创建非空约束
clazz_id = Column(Integer(), ForeignKey('clazz.id', ondelete='RESTRICT'), nullable=True, comment="班级ID") # 创建外键

clazz = relationship('Clazz') # 多对一(一对多)关系映射


class Course(Base):
id = Column(Integer(), primary_key=True, autoincrement=True)
name = Column(String(), comment="课程名")

students = relationship('Student', secondary='student_course', backref=backref('courses')) # 多对多映射关系,backref相当于给Student对象加了courses属性

def keys(self):
return super().keys() + ["students",]


# 选课(多对多关系表)
student_course = Table('student_course', Base.metadata,
Column('student_id', Integer(), ForeignKey('student.id'), primary_key=True),
Column('course_id', Integer(), ForeignKey('course.id'), primary_key=True))


def main():
engine = init_engine(url=r"sqlite:///:memory:")
Base.metadata.create_all(engine) # 根据定义创建出所有表
with session_scope() as session:
clazz = Clazz(enroll_year=2022) # 创建一个clazz对象
session.add(clazz) # 用clazz对象创建一行记录
session.flush() # 立即执行
session.refresh(clazz) # 更新clazz,把刚刚创建的一行记录的id取出来更新到对象中
print(clazz)

course = Course(name="数学")
session.add(course)
session.flush()
course = session.query(Course).filter(Course.name=="数学").first() # 把刚插入的记录查询出来
print(course)

student = Student(stu_no="007", name="张三", clazz_id=clazz.id)
student.courses.append(course) # 测试backref给student加了courses属性,并且可以通过其维护关系表
session.add(course)
session.flush()

session.refresh(course)
print(course.students)
print(to_jsonable(course)) #to_jsonable有students属性需要覆盖keys方法来支持

student.name = "李四"
session.flush() # 这里相当于执行了update


# 执行原生SQL查询
r = session.execute("select id, name from student").fetchall()
for e in r:
print(e) # 可以看到已经变成了李四



if __name__ == '__main__':
main()

使用sqlacodegen对已有数据库生成模型定义代码

通过pip安装

1
pip install sqlacodegen

命令格式 就是 sqlacodegen 后面跟初始化engine的url

1
sqlacodegen <sqldialect>[+<sqldriver>]://<username>:<password>@<server>/<dbname>

执行命令会在控制台输出生成的代码,可以重定向到.py

示例

1
sqlacodegen mysql+pymysql://mzdai:123456@192.168.1.140/mzdai > db_mzdai.py

然后可以修改生成的代码,例如导入db_中的一些内容,然后替换模型的基类 declarative_base(cls=ModelBase)

临时修改,重启失效

1
hostname xxx

永久修改,重启生效

1
vim /etc/hostname

域名解析文件一并修改

1
vim /etc/hosts

默认的源在国外,下载软件速度较慢,可以替换为国内的源,比如阿里的,替换 /etc/apt/sources.list文件为

1
2
3
4
5
6
7
8
9
10
11
12
# 阿里云镜像
deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
# 预发布版软件,可以不启用
deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse

目前3个主流版本的代号分别是

18.04 LTS bionic

20.04 LTS focal

22.04 LTS jammy

其他版本则把其中的bionic替换成其他版本的代号。

可以用tee命令直接修改文件,而不用vim或nano打开,以20.04 LTS,不带预发布版软件为例

1
2
3
4
5
6
7
8
9
10
tee /etc/apt/sources.list <<-'EOF'
deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
EOF

更新软件包

1
apt-get update

如果提示 NO_PUBKEY 3B4FE6ACC0B21F32

则执行

1
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32

Python的线程没有提供从外部主动终止线程的方法,当然从程序设计上应该避免这种方式,但有些时候我们希望把线程超时当做一种异常来处理,我们需要提前终止它,下面的代码提供了一种可参考方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import threading
import inspect
import ctypes

def _async_raise(tid, exctype):
"""raises the exception, performs cleanup if needed"""
tid = ctypes.c_long(tid)
if not inspect.isclass(exctype):
exctype = type(exctype)
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
if res == 0:
raise ValueError("invalid thread id")
elif res != 1:
# """if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"""
ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
raise SystemError("PyThreadState_SetAsyncExc failed")

def kill_thread(tid):
_async_raise(tid, SystemExit)

实际测试下来大多数情况是可以工作的,除了线程长时间陷在time.sleep的情况,也就是说上面的代码无法把一个线程从sleep状态唤醒并终止。

无监督学习算法指的是算法只有输入数据(Features),不需要用到(或根本不知道)输入的对应输出(Target),从中提取一些有用的知识。

sklearn中无监督学习算法主要分为以下类型:预处理、分解和聚类。

scikit-learn中的算法实现

算法中文名 所属模块 类名 主要参数
范围缩放 preprocessing MinMaxScaler feature_range=(0,1)
标准化缩放 preprocessing StandardScaler
One-Hot编码 preprocessing OneHotEncoder categorical_features = ‘all’
多项式特征 preprocessing PolynomialFeatures degree=2
主成分分析 decomposition PCA n_components, whiten=False
非负矩阵分解 decomposition NMF n_components
K聚类 cluster KMeans n_clusters
凝聚聚类 cluster AgglomerativeClustering n_clusters, linkage=ward
DBSCAN cluster DBSCAN min_samples, eps

可以看到上述算法所属模块即对应了预处理、分解和聚类。

预处理就是用于对监督学习算法的输入数据做前期处理,输入和输出都是一组Features。

事实上分解和聚类也可以作为监督学习算法的前期处理,不过他们也可以提供一些额外的功能。

下面对这些算法做逐一简要说明:

MinMaxScaler

根据最大最小值缩放和平移特征,默认参数使得每个特征都在0~1之间。

StandardScaler

根据均值和方差缩放和平移特征,默认参数使得每个特征的均值是0,方差是1。

OneHotEncoder

对分类变量(特征是离散的枚举值)进行编码,把一个具有N个枚举值的特征用N个0,1值的特征表示。

PolynomialFeatures

制造原特征的交互特征和多项式特征,例如(x1,x2)可以生成出(1, x1, x2, x1^2, x2^2, x1*x2),通过degree可以控制生成特征的最高次。可以让线性模型学习出对原特征来说非线性的结果。

PCA

主成分分析是找到原特征的一种新的表示,从线性代数的角度讲是找到一组正交基,然后把原特征当成向量,算出它们在新基下的表示,也就得到一组新表示下特征。这组正交基的取法是先找到原特征离散度最大的轴向,作为正交基的第一个轴,然后第二个轴是在与第一个轴“垂直”的超平面上,继续选取离散度最大的轴向,第三个轴要在与前两个轴都“垂直”的超平面上找离散度最大的轴向,以此类推。因为变换得到的新特征再前几个轴上离散程度较高后面依次降低,我们有理由期望新特征的前几个分量对Target的影响更大(虽然不一定),因此我们可以用PCA来对Feature降维(即丢弃掉后面离散程度最小的一些轴向上的坐标)。

NMF

非负矩阵分解原理类似PCA,不过它的基并不正交,NMF要求原特征均为非负,其基的每个分量和得到的新特征也均为非负,对于有多个独立源叠加而成的数据,比如多人说话的音轨或包含多种乐器的音乐,NMF能识别出组成合成数据的原始分量。NMF也可以用于降维。

KMeans

K均值聚类,所谓聚类就是把数据集按照一定的规则进行分组,使得同组的数据相似,不同组的数据相异,这些组在聚类算法中称为簇,K均值聚类要事先告知簇个数,算法的核心就是不停修正每个簇的簇中心,先随机选K个点作为簇中心,交替进行以下两个步骤:将每个数据点分配给最近的簇中心,然后将每个簇中心设置为所分配的所有数据点的平均值,重复以上步骤直至簇的分配不再变化。前面说聚类也可以作为监督学习算法的前期处理,这里如果我们用簇中心来代表簇里的数据点,那么每个点都可以用一个单一分量来表示(簇的编号),这称为矢量量化。(不过这种分量一般不具有连续意义,似乎还要再进行OneHot编码)

AgglomerativeClustering

凝聚聚类,先把每个数据点视作一个簇,然后按照一定的规则逐步合并,凝聚聚类算法可以生成可视的树状图来观察合并过程。凝聚聚类也需要提前告知簇个数。

DBSCAN

DBSCAN聚类,先随机选取一个没标记过的数据点标记一个新簇,以eps为距离做DFS让遍历到的数据点加入簇,如果最终遍历到的点少于min_samples则视为噪声,然后在再随机选取数据点再遍历再标记,进行直到所有点被标记到一个簇里或标记为噪声,DBSCAN可以生成具有复杂形状的簇,噪声也可以用来做异常值检测

K近邻

适用于小型数据集,基准模型,容易解释。不适用于高维稀疏数据,不能外推(超出训练数据集的范围进行预测)。

线性模型(最小二乘法、岭回归、Lasso回归、弹性网络、逻辑回归、线性支持向量机)

非常可靠的首选算法,适用于非常大的数据集,也适用于高维数据,可以外推。在低维空间中泛化性能可能很差(这还要看具体问题本身的特点,还可以通过扩展特征来增加线性模型的可用性)。

其中最小二乘法、岭回归、Lasso回归、弹性网络为回归器,不需要数据缩放。逻辑回归、线性支持向量机为分类器,逻辑回归如果不进行数据缩放会导致收敛较慢需要增加迭代次数,线性支持向量机需要进行数据缩放。

朴素贝叶斯(高斯朴素贝叶斯、伯努利朴素贝叶斯、多项式朴素贝叶斯)

只适用于分类问题。适用于非常大的数据集和高维数据,比线性模型速度快,精度低于线性模型。

决策树

速度很快,不需要数据缩放,可以可视化,很容易理解。不适用于高维稀疏数据,不能外推。

随机森林

几乎总是比单棵决策树的表现要好,鲁棒性很好(可以容忍训练集中有一些错误的数据),通常不需要反复调节参数就可以给出很好的结果,不需要数据缩放,不适用于高维稀疏数据,不能外推。

梯度提升机

是监督学习中最强大也最常用的模型之一。精度通常比随机森林略高,与随机森林相比,训练速度更慢,但预测速度更快,需要的内存更少,比随机森林需要更多的参数调节,不适用于高维稀疏数据,不能外推。

核支持向量机

是非常强大的模型,允许决策边界很复杂,在低维和高维数据集上的表现都很好。对于特征含义相似的中等大小(几千~几万这样的量级)的数据集很强大。需要数据缩放,对参数敏感,可以外推。

神经网络(多层感知机)

可以构建非常复杂的模型,特别是对于大型数据集而言。对于数据缩放敏感,对参数选取敏感。大型网络需要很长的训练时间。

scikit-learn中的算法实现

算法名 所属模块 分类器 回归器 主要参数(-号表示越小越模型复杂)
K近邻(KNN) neighbors KNeighborsClassifier KNeighborsRegressor -n_neighbors=5
最小二乘法 linear_model LinearRegression
岭回归 linear_model Ridge -alpha=1
Lasso回归 linear_model Lasso -alpha=1
弹性网络 linear_model ElasticNet -alpha=1, l1_ratio=0.5
逻辑回归 linear_model LogisticRegression +C=1.0
线性支持向量机 svm LinearSVC LinearSVR +C=1.0
高斯朴素贝叶斯 naive_bayes GaussianNB
伯努利朴素贝叶斯 naive_bayes BernoulliNB -alpha=1.0
多项式朴素贝叶斯 naive_bayes MultinomialNB -alpha=1.0
决策树 tree DecisionTreeClassifier DecisionTreeRegressor +max_depth
随机森林 ensemble RandomForestClassifier RandomForestRegressor +n_estimators
梯度提升机 ensemble GradientBoostingClassifier GradientBoostingRegressor +n_estimators, +learning_rate
核支持向量机 svm SVC SVR kernel=‘rbf’, +C, +gamma
多层感知机 neural_network MLPClassifier MLPRegressor solver=‘lbfgs’, -alpha, hidden_layer_sizes

shader里面有个函数叫smoothstep,是用来做平滑插值的,dx的文档对其介绍如下

smoothstep (DirectX HLSL)

Returns a smooth Hermite interpolation between 0 and 1, if x is in the range [min, max].

ret smoothstep(min, max, x)

Parameters

  • min

    [in] The minimum range of the x parameter.

  • max

    [in] The maximum range of the x parameter.

  • x

    [in] The specified value to be interpolated.

Return Value

Returns 0 if x is less than min; 1 if x is greater than max; otherwise, a value between 0 and 1 if x is in the range [min, max].

Remarks

Use the smoothstep HLSL intrinsic function to create a smooth transition between two values. For example, you can use this function to blend two colors smoothly.

可以看介绍里说了,Smoothstep就是使用的Hermite插值,
三次Hermite插值公式是

P(t)=(2t33t2+1)P0+(t32t2+t)M0+(t3t2)M1+(2t3+3t2)P1P(t) = (2*t^3 - 3*t^2 + 1)P0 + (t^3 - 2*t^2 + t)M0 + (t^3 - t^2)M1 + (-2*t^3 + 3*t^2)P1

其中P0是起始点,P1是终结点,M0是起始点处的方向,M1是终结点处的方向。参数t从0变化到1的过程中P(t)形成的轨迹构成了从P0到P1的平滑曲线,而且这个曲线两端顶点处的切向量就是M0,M1,还要说明的就是M0,M1的大小会影响到曲线的形状,可以把这个过程想象成一个机车从P0开到P1,M0,M1更像是速度的表示,当M0比较大时,在P0附近的曲线会沿M0方向冲出去更多一些才会弯曲。
Smoothstep插值的公式是

Smoothstep(t)=2t3+3t2Smoothstep(t) = -2*t^3 + 3*t^2

对于dx文档中的参数,我们可以使用

t=(xmin)/(maxmin)t = (x-min) / (max-min)

先计算得到t再使用前面的公式。

为什么Smoothstep的公式比Hermite简化的这么多,我尝试推导了一下,
把(0,0)代入P0,(1,1)代入P1,(1,0)代入M0,(1,0)代入M1,
用Px(t)表示Hermite插值的x分量,Py(t)表示Hermite插值的y分量,则有

Px(t)=(2t33t2+1)0+(t32t2+t)1+(t3t2)1+(2t3+3t2)1=tP_x(t) = (2*t^3 - 3*t^2 + 1)*0 + (t^3 - 2*t^2 + t)*1 + (t^3 - t^2)*1 + (-2*t^3 + 3*t^2)*1 = t

Py(t)=(2t33t2+1)0+(t32t2+t)0+(t3t2)0+(2t3+3t2)1=2t3+3t2P_y(t) = (2*t^3 - 3*t^2 + 1)*0 + (t^3 - 2*t^2 + t)*0 + (t^3 - t^2)*0 + (-2*t^3 + 3*t^2)*1 = -2*t^3 + 3*t^2

再把参数t消掉就可以得到

Py=2Px3+3Px2P_y = -2*Px^3 + 3*P_x^2

和Smoothstep插值公式是一致的。

我把这篇旧博文转过来主要为了测试在网页上渲染Markdown公式的表现 😛

* boost 标准库扩展,广为人知的“准”标准库
* pthread windows下的posix线程实现
* libcurl 文件传输库,支持多种协议。
* libeay32 OpenSSL Library
* libtidy,htmlcxx 解析html的库
* zlib 数据压缩库,本数以千计的软件广泛使用,已成为一种事实上的业界标准。
* freetype C接口的type2字体处理库
* libmad 一个编解码mp3的库
* libogg 一个编解码ogg音频格式的库
* libsnd 一个开源的编解码十多种音频格式的库
* ffmpeg 一个音视频格式编解码、转换的库
* FreeImage,CxImage 图像操作类库。它可以快捷地存取、显示、转换各种图像。
* libpng,libjpeg 图片的编码解码
* angelscript 一个类似lua的脚本引擎 其脚本风格类似于标准c语言
* flac/flac++ 一个编解码flac音频格式的库
* tinyxml,rapidxml,libxml 都是关于xml解析方面的
* luaplus,luabind 都是涉及绑定lua和c的库
* ode,bullet 开源的物理引擎库
* timidity 一个可以把mid音频格式转化为wav格式的库
* vlc 一个视频播放的库
* zthread 一个类型boost-thread,pthread的c
风格的多线程库
* SDL 一个自由的跨平台的多媒体开发包,主要做音视频播放
* HGE Windows下基于d3d硬件加速的2d游戏引擎,基于DX8,已经停止维护很久了
* OpenCV 一个开源的图像处理库,实现了图像处理和计算机视觉方面的很多通用算法。
* mygui,cegui 都是游戏上使用的GUI系统
* Orge,irrlicht 都是开源的游戏中间件
* Qt,wxWidgets 开源的跨平台的C构架库,主要是做跨平台GUI
* loki 一个实验性质的c

* ace 一个网络通信库
* FMOD 游戏音频引擎
* SQLite 一款轻型的数据库
* AmHttpSocket 基于WinAPI的简便http协议应用包