机器学习与大数据赛项
竞赛环境:
名称
技术规格
Hadoop
3.3.x
Sqoop
1.4.x
Hive
2.3.x
Zookeeper
3.6.x
Flume
1.10.x
Spark
3.3.x
Flink
1.15.x
JDK
11
Scala
2.13.x
Python
3.7.x
比赛内容:
Hadoop 平台搭建、HDFS 数据存储、Flume 数据采集、Sqoop 数据导入、MapReduce 批处理操作、Hive 数据查询、大数据 可视化、数据整合、数据读取、数据探查、数据处理、数据 建模、模型评价。
模块A:大数据运维与应用(30分)
三天把完全分布式自己搭建了一遍,中间mysql的安装是搞了好久,hive启动失败,多半是mysql有错误,mysql那里还是要多看看。剩下的时间就是背加练了,没什么好说的
Hadoop基础环境搭建
参考链接: https://blog.csdn.net/qq_44715376/article/details/130843233?csdn_share_tail={"type"%3A"blog"%2C"rType"%3A"article"%2C"rId"%3A"130843233"%2C"source"%3A"qq_44715376"}&fromshare=blogdetail
1、安装虚拟机和Linux操作系统,配置IP地址、主机名、防火墙、地址映射等
1 2 3 4 5 6 7 网卡 vi /etc/sysconfig/network-scripts/ifcfg-ens33 /ect/hosts 配置主机名和IP地址的对应,对本机提供解析 /etc/resolv.conf 配置域名(在hosts内解析不到时此域名生效) /etc/sysconfig/network 配置主机名和网关 /etc/sysconfig/network-scripts/ifcfg-eth0 配置IP、Mask等网络参数
1 2 3 4 5 重启网卡 systemctl restart netword 查看网卡 ip addr 或 ifconfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 主机名 :/etc/hostname master 映射:/etc/hosts 192.168.43.10 master 192.168.43.20 node01 192.168.43.30 node02 关闭防火墙 systemctl stop firewalld systemctl disable firewalld 关闭Linunx安全子系统 vi /etc/selinux/config SELINUX=disabled 重启系统 reboot
2、安装 JDK 与 Hadoop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 上传安装包 解压 tar -zxvf jdk-8u261-linux-x64.tar.gz -C /opt/softwore/ tar -zxvf hadoop-2.7.7\ .tar.gz -C /opt/softwore 修改配置文件 vim + /etc/profile # JAVA_HOME export JAVA_HOME=/usr/local/src/jdk export PATH=$PATH:$JAVA_HOME/bin # HADOOP_HOME export HADOOP_HOME=/usr/local/src/hadoop export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin export HDFS_NAMENODE_USER=root export HDFS_DATANODE_USER=root export HDFS_SECONDARYNAMENODE_USER=root export YARN_RESOURCEMANAGER_USER=root export YARN_NODEMANAGER_USER=root 重新加载配置文件,使配置生效 source /etc/profile
3、完全分布式部署(HDFS)
1 2 3 4 5 6 7 8 配置文件路径:/usr/local/src/hadoop/etc/hadoop HDFS 修改 hadoop-env.sh 设置 Hadoop 环境对应的 JDK export JAVA_HOME=/usr/local/src/jdk 配置 MapReduce 与 YARN 修改 yarn-env.sh、mapred-env.sh 添加 JAVA_HOME 配置 export JAVA_HOME=/usr/local/src/jdk
修改 core-site.xml 配置文件,可以使用 NotePad++ 进行配置
1 2 3 4 5 6 7 8 9 10 11 12 <configuration > <property > <name > fs.defaultFS</name > <value > hdfs://192.168.201.10:8020</value > </property > <property > <name > hadoop.tmp.dir</name > <value > /usr/local/src/hadoop/data/tmp</value > </property > </configuration >
修改 hdfs-site.xml 配置文件
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 <configuration > <property > <name > dfs.replication</name > <value > 3</value > </property > <property > <name > dfs.name.dir</name > <value > /usr/local/src/hadoop/data/namenode</value > </property > <property > <name > dfs.data.dir</name > <value > /usr/local/src/hadoop/data/datanode</value > </property > <property > <name > dfs.tmp.dir</name > <value > /usr/local/src/hadoop/data/tmp</value > </property > <property > <name > dfs.namenode.secondary.http-address</name > <value > master:50090</value > </property > </configuration >
mapred-site.xml ,把 mapred-sit.xml.template 复制一份,修改为 mapred-site.xml yarn-site.xml 添加相应 配置
1 2 3 4 5 6 7 8 <configuration > <property > <name > mapreduce.framework.name</name > <value > yarn</value > </property > </configuration >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <configuration > <property > <name > yarn.nodemanager.aux-services</name > <value > mapreduce_shuffle</value > </property > <property > <name > yarn.resourcemanager.hostname</name > <value > master</value > </property > </configuration >
在 slaves 配置文件中 添加 主机名,它指定了 DataNode 和 NodeManager所在的机器。(注意这里有可能自带workers需要修改workers)
克隆
分发
1 scp -r /usr/local/hadoop-3.3.1 root@slave03:/usr/local
4、SSH免密登录
1 2 先在每一台节点生成公钥和私钥 ssh-keygen
1 2 3 4 把每一个节点上的公钥文件发送到所有节点(包括自己),注意:每一条命令都需要在3个节点中执行 ssh-copy-id master ssh-copy-id node01 ssh-copy-id node02
5、格式化HDFS
1 2 #master hdfs namenode -format
6、启动服务
1 2 3 start-dfs.sh /start-yarn.sh start-all.sh
7、查看进程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 jps #master [root@master hadoop]# jps 6002 SecondaryNameNode 6435 NodeManager 6280 ResourceManager 5594 NameNode 7835 Jps 5775 DataNode #node01 [root@node01 hadoop]# jps 2131 DataNode 2532 Jps 2271 NodeManager #node02 [root@node02 hadoop]# jps 2145 NodeManager 2405 Jps 2023 DataNode
1 2 3 4 5 6 7 8 9 10 11 12 Hadoop常用端口说明: hadoop2.x HDFS Namenode内部通常端口:8020/9000 HDFS Namenode对用户的查询端口:50070 Yarn查看任务运行情况的:8088 历史服务器:19888 hadoop3.x HDFS Namenode内部通常端口:8020/9000/9820 HDFS Namenode对用户的査询端口:9870 Yarn查看任务运行情况的:8088 历史服务器:19888
MySQL离线安装
安装包mysql-5.7.20-linux-glibc2.12-x86_64.tar.gz
拷贝到 离线生产环境/usr/local
目录下。
==1、创建用户 ==
1 2 3 4 5 6 7 8 groupadd mysql useradd -g mysql mysql -d /home/mysql passwd mysql
==2、解压缩==
1 2 3 4 5 6 7 8 9 10 11 12 13 cd /usr/local/ tar -xzvf mysql-5.7 .13 -linux-glibc2.5 -x86_64 mv mysql-5.7 .13 -linux-glibc2.5 -x86_64 mysql chown -R mysql:mysql mysql/ rpm -qa | grep mariadb yum remove mariadb*
==3、创建配置文件==
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 vim /etc/my.cnf [client] port = 3306 socket = /tmp/mysql.sock [mysqld] character_set_server=utf8 init_connect='SET NAMES utf8' basedir=/usr/local/src/mysql datadir=/usr/local/src/mysql/data socket=/tmp/mysql.sock log-error=/var/log/mysqld.log pid-file=/var/run/mysqld/mysqld.pid lower_case_table_names = 1 sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION max_connections=5000 explicit_defaults_for_timestamp=true default-time_zone = '+8:00'
==初始化文件 ==
1、初始化log文件,防止没有权限
1 2 3 4 5 6 7 8 cd /var/log/ vim mysqld.log :wq chmod 777 mysqld.log chown mysql:mysql mysqld.log
2、初始化pid文件,防止没有权限
1 2 3 4 5 6 7 8 9 10 cd /var/run/ mkdir mysqld cd mysqld vi mysqld.pid :wq cd .. chmod 777 mysqld chown -R mysql:mysql mysqld
创建/opt/module/mysql/data
3、初始化数据库
1 2 3 4 5 6 7 8 /usr/local/src/mysql/bin /mysqld --initialize --user=mysql --basedir=/usr/local/src/mysql --datadir=/usr/local/src/mysql/data --lc_messages_dir=/usr/local/src/mysql/share --lc_messages=en_US cat /var/log/mysqld.log
bash_profile 在 ~ /中
==启动数据库 ==
1 2 /usr/local/src/mysql/support-files/mysql.server start
设置开机自动启动服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 cp /usr/local/src/mysql/support-files/mysql.server /etc/rc.d/init.d/mysqld chmod +x /etc/rc.d/init.d/mysqld chkconfig --add mysqld chkconfig --list mysqld su mysql service mysqld start
==配置环境变量 ==
1 2 3 4 5 6 7 8 su mysql vi .bash_profile source .bash_profile
==登录,修改密码 ==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ln -s /usr/local/usr/mysql/bin /mysql /usr/bin mysql -uroot -p set password for root@localhost=password("123456" );mysql>grant all privileges on *.* to '新用户名' @'%' identified by '新密码' ; mysql>flush privileges;
Hive安装
1)解压
1 tar -zxvf /opt/softwares/apache-hive-3.1.2-bin.tar.gz -C /opt/module
2)配环境变量
1 2 3 export HIVE_HOME=/opt/module/hive export PATH=$PATH:$HIVE_HOME/bin export HADOOP_CLASSPATH='hadoop classpath'
3)修改配置文件
hive-env.sh
1 2 3 4 export JAVA_HOME=/opt/module/jdk1.8 export HIVE_HOME=/opt/module/hive export HADOOP_HOME=/opt/module/hadoop-3.1.3 export HIVE_CONF_DIR=/opt/module/hive/conf
hive-site.xml(可去hive/hcatalog/etc/hcatalog/目录下把proto-hive-site.xml复制到conf目录下并改名为hive-site.xml,删除第一个property之间的和除这四个以外的)
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 <property> <name>javax.jdo.option.ConnectionURL</name> <value>jdbc:mysql://master:3306/hive_db?createDatabaseIfNotExist=true&useSSL=false</value> </property> <property> <name>javax.jdo.option.ConnectionUserName</name> <value>root(名字看题目要求)</value> </property> <property> <name>javax.jdo.option.ConnectionPassword</name> <value>000000(密码看题目要求)</value> </property> <property> <name>javax.jdo.option.ConnectionDriverName</name> <value>com.mysql.jdbc.Driver</value> </property> <!--cli 显示表头和列名--> <property> <name>hive.cli.print.header</name> <value>true</value> </property> <property> <name>hive.cli.print.current.db</name> <value>true</value> </property>
4)拷贝jar包
1 2 3 cp mysql-connector-java-5.1.37-bin.jar /opt/module/hive/lib rm -rf /opt/module/hive/lib/guave-19.0.jar cp /opt/module/hadoop-3.1.3/share/hadoop/commond/lib/guava-27.0-jre.jar /opt/module/hive/lib
5)初始化
在src下
1 schematool -dbType mysql -initSchema
6)查看
输入hive,出现hive>
7)退出 exit;
登录
更改密码
1 2 alter user 'root'@'localhost' identified by 'root'; set password for 'root'@'localhost'='123456';
开启远程登录
1 2 3 4 grant all privileges on *.* to 'root'@'%' identified by 'root' with grant option; 刷新 flush privileges;
3、配置hive
1 2 配置 conf/hive-env.sh文件中的 HADOOP_HOME export HADOOP_HOME=/usr/local/hadoop/
4.导入jar包 和 移除jar包
1 将 mysql-connector-java-5.1.47.jar 添加到hive lib 下
1 2 3 /usr/local/hive/lib/ /usr/local/hadoop/share/hadoop/common/lib/ 删除低版本的jar包,将高版本的jar包复制到原来低版本的位置即可
5.修改hive-site.xml配置文件
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 <?xml version="1.0" encoding="UTF-8" ?> <configuration > <property > <name > javax.jdo.option.ConnectionURL</name > <value > jdbc:mysql://mater:3306/hive?createDatabaseIfNotExist=true& useSSL=false</value > </property > <property > <name > javax.jdo.option.ConnectionDriverName</name > <value > com.mysql.jdbc.Driver</value > </property > <property > <name > javax.jdo.option.ConnectionUserName</name > <value > root</value > </property > <property > <name > javax.jdo.option.ConnectionPassword</name > <value > 123456</value > </property > </configuration >
Hive使用
==1、外部表 ==
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 CREATE EXTERNAL TABLE t1 ( cust_id STRING, fname STRING, lname STRING, email STRING, level STRING, phone MAP< STRING, STRING> , order_ids ARRAY < STRING> , order_value STRUCT< min: STRING, max: STRING, avg: STRING, total: STRING> ) row format delimitedfields terminated by '|' collection items terminated by ',' map keys terminated by ':' location '/yyh' ; CREATE EXTERNAL TABLE t1 ( cust_id STRING, fname STRING, lname STRING, email STRING, level STRING, phone MAP< STRING, STRING> , order_ids ARRAY < STRING> , order_value STRUCT< min: DOUBLE , max: DOUBLE , avg: DOUBLE , total: DOUBLE > ) row format delimitedfields terminated by '|' collection items terminated by ',' map keys terminated by ':' location '/yyh' ; CREATE EXTERNAL TABLE t5 ( cust_id STRING, fname STRING, lname STRING, email STRING, level STRING, phone MAP< STRING, STRING> , order_ids ARRAY < STRING> , order_value STRUCT< min: DOUBLE , max: DOUBLE , avg: DOUBLE , total: DOUBLE > ) row format delimitedfields terminated by '|' collection items terminated by ',' map keys terminated by '#' location '/yyh' ; INSERT OVERWRITE LOCAL DIRECTORY '/home/ec2-user/results' ROW FORMAT DELIMITEDFIELDS TERMINATED BY ',' SELECT phone['HOME' ] FROM loyalty_program WHERE cust_id = '1200866' ;SELECT phone['HOME' ] FROM loyalty_program WHERE cust_id = '1200866' ;SELECT order_ids[2 ] FROM loyalty_program WHERE cust_id = '1200866' ;SELECT order_value.total FROM loyalty_program WHERE cust_id = '1200866' ;
==分析数值产品评分 ==
1 2 3 4 5 6 7 8 9 10 11 12 13 SELECT prod_idFROM ( SELECT prod_id, AVG (rating) AS avg_rating, COUNT (* ) AS rating_count FROM ratings GROUP BY prod_id HAVING rating_count >= 50 ) t ORDER BY avg_rating DESC LIMIT 1 ;
Sqoop安装与使用
==1、上传安装包、解压 ==
1 2 3 tar -zxvf sqoop-1.4.7.bin__hadoop-2.6.0.tar.gz mv sqoop-1.4.7.bin__hadoop-2.6.0 sqoop
==2、拷贝jar包,MySQL,hive ==
1 cp mysql-connector-java-5.1.46.jar hive-exec-2.3.3.jar /opt/softwore/sqoop/lib
==3、配置环境变量 ==
1 2 3 4 5 6 vim / etc/ profile ## SQOOP_HOME export SQOOP_HOME= / opt/ softwore/ sqoop export PATH= $PATH:$SQOOP_HOME/ bin source / etc/ profile
==4、修改配置 文件 ==
1 2 3 4 5 6 cp sqoop-env-template.sh sqoop-env.sh 修改以下内容 export HADOOP_COMMON_HOME=/opt/softwore/hadoop export HADOOP_MAPRED_HOME=/opt/softwore/hadoop export HIVE_HOME=/opt/softwore/hive
==5、测试环境 ==
==6、列出数据库 ==
1 2 3 sqoop list- databases \
==7、查询SQL ==
1 2 3 4 5 sqoop eval \ --connect jdbc:mysql://master:3306/yyh \ --username root --password 123456 \ --query 'select count(distinct(name)) from t1' > ~/results/task3-1.txt
==8、sqoop导入 ==
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 # MYSQL导入到HDFS sqoop import \ --connect jdbc:mysql://master:3306/yyh \ --username root \ --password 123456 \ --table t1 \ --fields-terminated-by ',' \ --lines-terminated-by '\n' \ --target-dir '/sqoop/t1' \ -m 1 # MYSQL导入到Hive sqoop import \ --connect jdbc:mysql://master:3306/yyh \ --username root \ --password 123456 \ --table t1 \ --fields-terminated-by ',' \ --lines-terminated-by '\n' \ --target-dir '/sqoop/t111' \ --delete-target-dir \ --hive-import \ --hive-table 'yyh.t111' \ -m 1
==9、sqoop导出 ==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # HDFS导出到MYSQL sqoop export \ --connect jdbc:mysql://master:3306/yyh \ --username root \ --password 123456 \ --table t1 \ --export-dir '/sqoop/t1/*' \ --fields-terminated-by ',' \ --lines-terminated-by '\n' \ -m 1 # Hive导出到MYSQL sqoop export \ --connect jdbc:mysql://master:3306/yyh \ --username root \ --password 123456 \ --table t111 \ --export-dir '/user/hive/warehouse/yyh.db/t11/*' \ -m 1
==报错 ==
1 2 [root@master txt]# hadoop classpath /opt/softwore/hadoop/etc/hadoop:/opt/softwore/hadoop/share/hadoop/common/lib/*:/opt/softwore/hadoop/share/hadoop/common/*:/opt/softwore/hadoop/share/hadoop/hdfs:/opt/softwore/hadoop/share/hadoop/hdfs/lib/*:/opt/softwore/hadoop/share/hadoop/hdfs/*:/opt/softwore/hadoop/share/hadoop/mapreduce/*:/opt/softwore/hadoop/share/hadoop/yarn:/opt/softwore/hadoop/share/hadoop/yarn/lib/*:/opt/softwore/hadoop/share/hadoop/yarn/*
修改yarn-site.xml
1 2 3 <property > <name > yarn.application.classpath</name > <value > /opt/softwore/hadoop/etc/hadoop:/opt/softwore/hadoop/share/hadoop/common/lib/*:/opt/softwore/hadoop/share/hadoop/common/*:/opt/softwore/hadoop/share/hadoop/hdfs:/opt/softwore/hadoop/share/hadoop/hdfs/lib/*:/opt/softwore/hadoop/share/hadoop/hdfs/*:/opt/softwore/hadoop/share/hadoop/mapreduce/*:/opt/softwore/hadoop/share/hadoop/yarn:/opt/softwore/hadoop/share/hadoop/yarn/lib/*:/opt/softwore/hadoop/share/hadoop/yarn/*</value > </property >
Spark安装
安装和启动 Spark 组件包括以下步骤:
解压 Spark 安装包: 在 master 节点上执行以下命令解压 Spark 安装包:
1 tar -zxvf ~/hadoop搭建相关安装包/spark-2.4.6-bin-hadoop2.7.tgz
配置 Spark 环境变量: 编辑 ~/.bashrc
文件,并添加以下环境变量配置:
1 2 export SPARK_HOME=/path/to/spark-2.4.6-bin-hadoop2.7 export PATH=$PATH:$SPARK_HOME/bin
然后运行以下命令使环境变量生效:
配置 Spark 主节点: 在 Spark 安装目录中的 conf
文件夹中,复制 spark-env.sh.template
并将其重命名为 spark-env.sh
:
1 2 cd /path/to/spark-2.4.6-bin-hadoop2.7/conf cp spark-env.sh.template spark-env.sh
编辑 spark-env.sh
文件,添加以下内容来配置 Spark 主节点(master):
1 2 3 export SPARK_MASTER_HOST=master_node_hostname export SPARK_MASTER_PORT=7077 export SPARK_MASTER_WEBUI_PORT=8080
将 master_node_hostname
替换为 master 节点的主机名或 IP 地址。
配置 Spark 工作节点: 在 Spark 安装目录中的 conf
文件夹中,复制 slaves.template
并将其重命名为 slaves
:
1 2 cd /path/to/spark-2.4.6-bin-hadoop2.7/conf cp slaves.template slaves
编辑 slaves
文件,添加以下内容来配置 Spark 工作节点(worker):
将 node1
和 node2
替换为实际的工作节点主机名或 IP 地址。
启动 Spark 主节点和工作节点: 在 master 节点上执行以下命令启动 Spark 主节点(Master):
1 /path/to/spark-2.4.6-bin-hadoop2.7/sbin/start-master.sh
然后,在每个工作节点(node1 和 node2)上执行以下命令启动 Spark 工作节点(Worker):
1 /path/to/spark-2.4.6-bin-hadoop2.7/sbin/start-slave.sh spark://master_node_hostname:7077
将 master_node_hostname
替换为实际的 master 节点主机名或 IP 地址。
验证 Spark 启动: 在浏览器中访问 Spark 主节点的 Web UI,地址为 http://master_node_hostname:8080
,确保 Spark 主节点和工作节点都已成功启动,并查看集群的状态。
完成上述步骤后,Spark 组件将安装在 master 节点和两个工作节点上,并且 Spark 集群已经启动,包含一个主节点(Master)和两个工作节点(Worker)。你可以通过 Web UI 来监控和管理 Spark 集群,并在 Spark Shell 中执行 Spark 任务。
==1、修改Hadoop配置文件yarn-site.xml ==
1 2 3 4 5 6 7 8 9 10 <property > <name > yarn.nodemanager.pmem-check-enabled</name > <value > false</value > </property > <property > <name > yarn.nodemanager.vmem-check-enabled</name > <value > false</value > </property >
==2、解压安装包、配置 ==
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 解压spark,并重命名为spark-yarn tar -zxvf spark-2.4.7-bin-hadoop2.7.tgz mv spark-2.4.7-bin-hadoop2.7 spark-yarn 修改spark-env.sh文件,添加java环境和yarn配置地址 cp spark-env.sh.template spark-env.sh vim spark-env.sh 添加内容 export JAVA_HOME=/opt/softwore/jdk8 export HADOOP_HOME=/opt/softwore/hadoop export HADOOP_CONF_DIR=/opt/softwore/hadoop/etc/hadoop export LD_LIBRARY_PATH=/opt/softwore/hadoop/lib/native export SPARK_MASTER_IP=master export SPARK_LOCAL_DIRS=/opt/softwore/src/spark/tmp export SPARK_LOG_DIR=/opt/softwore/spark/logs export SPARK_MASTER_PORT=7077 export SPARK_DRIVER_MEMORY=512M 修改slaves文件 cp slaves.template slaves vim slaves node01 node02 修改环境变量 vim /etc/profile ## SPARK_ENV export SPARK_HOME=/usr/local/spark-yarn export PATH=$PATH:$SPARK_HOME/bin:$SPARK_HOME/sbin 刷新,生效 source /etc/profile
==3、导入jar包 ==
1 2 cp mysql-connector-java-5 .1 .47 .jar /opt/softwore/spark/jarscp hive-common-2 .3 .1 .jar /opt/softwore/spark/jars
==4、使用Hive ==
1 2 3 4 5 6 7 8 9 spark-shell :paste saprk.sql("select * from yyh.t1" ).show() ctrl + D
Spark使用
省赛样卷一
任务6:Spark 分布式计算(5分)
6.1 计算不重复的电影数量
6.2 计算电影平均评分
6.3 计算用户在夜晚的评分总记录数
==实例化对象 ==
6.1 计算不重复的电影数量
在master节点上操作,读取 hdfs 上/spark/movie_ratings.csv数据,启动 spark 计算不重复的电影(movieId)数量,并保存结果到~/results/task6-1.txt文件中。(1分)
spark-shell
还是先开启多行代码编辑模式:paste
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 scala> :paste val data = spark.read .option("header" ,"true" ) .option("inferSchema" , "true" ) .format("csv" ) .load("hdfs:///spark/movie_ratings.csv" ) .createTempView("t1" ) spark.sql("select count(distinct(movieId)) from t1" ).show() val res1 = spark.sql("select count(distinct(movieId)) from t1" )Seq (res1.first().toString()).toDF().write.mode("overwrite" ).text("file:///root/results/task6-2.txt" )+-----------------------+ |count(DISTINCT movieId)| +-----------------------+ | 14026 | +-----------------------+ val data: Unit = ()val res1: org.apache.spark.sql.DataFrame = [count(DISTINCT movieId): bigint]
保存文件的时候遇到了一些问题,因为前面我是设置了.format("inferSchema", "true")
spark自动识别字段类型,所以有的数据格式为Bigint
,但是存文件时候spark不支持直接将Bigint
类型转换为文本格式,这里想了想只是输出一个纯文本用不着这么麻烦我直接就强转了,但是这样是错的你强转的只是sql语句,所以还是需要麻烦一点获取first()
其实更好的方式是将Bigint
类型转成String,然后存到一个Seq中再转成dataframe
格式
还有一点就是保存到本地是file:///
Seq
提供了许多其他的操作方法,如head
、tail
、isEmpty
、distinct
等
需要注意的是,Seq
是不可变的,这意味着它的元素是不可变的,一旦创建,就不能修改。如果你需要可变的序列,可以使用mutable.Seq
。
1 SELECT from_unixtime(1112484727 , 'yyyy-MM-dd HH:mm:ss' );
1 2 val data1_1 = spark.sql("select cast(count(distinct(movieId)) as string) as number from t1" ) data1_1.write.option("header" ,"false" ).format("text" ).mode("overwrite" ).save("file:///root/spark/s1.txt" )
6.2 计算电影平均评分
在master节点上操作,读取 hdfs 上/spark/movie_ratings.csv数据,运用 spark 计算电影平均评分,保留两位小数,并保存结果到~/results/task6-2.txt文件中。(平均评分=总评分数/总评分记录数)(1分)
1 2 val res2 = spark.sql("select round(avg(rating),2) as avg_rating from t1" )Seq (res2.first().toString()).toDF().write.mode("overwrite" ).text("file:///root/results/task6-2.txt" )
6.3 计算用户在夜晚的评分总记录数
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 val data = spark.read .option("header" , "true" ) .option("inferSchema" , "true" ) .csv("C:\\Users\\beihai\\Desktop\\competition\\movie_ratings.csv" ) .createTempView("view1" ) spark.sql( """ |with t1 as ( |select | userId,movieId,rating,hour(from_unixtime(timestamp, 'yyyy-MM-dd HH:mm:ss')) as hour_time |from | view1 |), |t2 as ( |select | userId,rating, |case | when hour_time >= 7 and hour_time < 12 then 'morning' | when hour_time >= 12 and hour_time < 14 then 'lunch' | when hour_time >= 14 and hour_time < 18 then 'afternoon' | when hour_time >= 18 and hour_time < 24 then 'evening' | else 'night' |end as evening_rating |from t1 |) |select |userId, sum(rating) as rating |from t2 |where evening_rating = 'evening' |group by userId |order by userId |""" .stripMargin).show() spark.sql( """ |select | userId,sum(rating) as evening_ratings |from view1 |where | hour(from_unixtime(timestamp,'yyyy-MM-dd HH:mm:ss')) >= 18 and hour(from_unixtime(timestamp,'yyyy-MM-dd HH:mm:ss')) < 24 |group by userId |order by userId |""" .stripMargin).show()import org.apache.spark.sql.functions._val data = spark.read .option("header" , "true" ) .option("inferSchema" , "true" ) .csv("hdfs:///spark/movie_ratings.csv" ) val formattedData = data.withColumn("timestamp" , to_timestamp(col("timestamp" ), "yyyy-MM-dd HH:mm:ss" ))val categorizedData = formattedData.withColumn("time_period" , when(hour(col("timestamp" )).between(7 , 11 ), "morning" ) .when(hour(col("timestamp" )).between(12 , 13 ), "lunch" ) .when(hour(col("timestamp" )).between(14 , 17 ), "afternoon" ) .when(hour(col("timestamp" )).between(18 , 23 ), "evening" ) .otherwise("night" )) val eveningCount = categorizedData.filter(col("time_period" ) === "evening" ).count()val outputPath = "/home/ec2-user/results/task6-3.txt" val result = s"Evening rating count: $eveningCount " spark.sparkContext.parallelize(Seq (result)).saveAsTextFile(outputPath) scala> spark.sql(""" | select | userId,count(*) as countt | from df1 | where hour(from_unixtime(timestamp,'yyyy-MM-dd HH:mm:ss')) >= 18 and | hour(from_unixtime(timestamp,'yyyy-MM-dd HH:mm:ss')) < 24 | group by userId | """ ).show()+------+------+ |userId|countt| +------+------+ | 148 | 128 | | 463 | 79 | | 471 | 31 | | 833 | 3 | | 1088 | 60 | | 1342 | 25 | | 1591 | 4 | | 1959 | 74 | | 2122 | 46 | | 392 | 21 | | 540 | 10 | | 737 | 19 | | 858 | 62 | | 1084 | 94 | | 1127 | 82 | | 1896 | 116 | | 1143 | 70 | | 1270 | 78 | | 1322 | 208 | | 1339 | 385 | +------+------+ only showing top 20 rows
Flume安装与使用
==1、上传安装包,解压配置 ==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 tar - zxvf apache- flume-1.9 .0 - bin.tar.gz mv apache- flume-1.8 .0 - bin flume 修改配置文件 cp flume- env.sh.template flume- env.sh vim flume- env.sh export JAVA_HOME= / usr/ local / src/ jdk 配置环境变量 vim / etc/ profile ## FLUME_HOME export FLUME_HOME= / usr/ local / flume export PATH= $PATH:$FLUME_HOME/ bin 测试 Flume 环境配置 flume- ng version
==2、Flume 入门案例 ==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Namenode 端口: 2. x端口 3. x端口 name desc 50470 9871 dfs.namenode.https- address The namenode secure http server address and port.50070 9870 dfs.namenode.http- address The address and the base port where the dfs namenode web ui will listen on.8020 9820 fs.defaultFS 指定HDFS运行时nameNode地址Secondary NN 端口: 2. x端口 3. x端口 name desc 50091 9869 dfs.namenode.secondary.https- address The secondary namenode HTTPS server address and port50090 9868 dfs.namenode.secondary.http- address The secondary namenode HTTPS server address and portDatanode 端口: 2. x端口 3. x端口 name desc 50020 9867 dfs.datanode.ipc.address The datanode ipc server address and port.50010 9866 dfs.datanode.address The datanode server address and port for data transfer.50475 9865 dfs.datanode.https.address The datanode secure http server address and port50075 9864 dfs.datanode.http.address The datanode http server address and porYarn 端口 2. x端口 3. x端口 name desc 8088 8088 yarn.resourcemanager.webapp.address http服务端口
Linux权限
https://blog.csdn.net/qq_63992711/article/details/127042653
1 2 3 rwx rw- r-- 这就是 111 110 100 最终即 764 权限。 修改file.txt文件的权限分配设置为 rwx rw- r-- 就可以操作为 chmod 764 file.txt
模块B:数据分析(30分)
花了两天把省赛样题一做了一遍,感觉还不错,难点就是在前期的数据的处理上,对于所处数据的格式要有分辨,是对列处理,还是对表处理。另外对数据异常值和空值的处理时候会出现问题,就可以用替换的方式,fillna处理的时候要注意替换成数值类型的时候,需要将源数据的格式astype为数值类型。对于画图这里,其实还是对数据进行处理,难点就是对于轴处理和值处理,对于x,y最好还是zip一下或者enumerate。这一模块练的就是对数据的敏感性,还是得多刷点算法题练思维
省赛样卷一:模块B
1 2 3 4 5 6 7 8 9 import numpy as npimport pandas as pdfrom matplotlib import pyplot as pltimport jsonplt.rcParams['font.sans-serif' ] = ['SimHei' ] plt.rcParams['axes.unicode_minus' ] = False
任务1:数据读取(1分)
这里任务书说着是只读取一个文件,到比赛很可能就是读取两张表进行合并
1 2 3 4 5 6 7 8 """ 任务1:数据读取(1分) • 载入 movies.csv 文件,数据路径为 ../data/movies.csv • 结果以变量data保存 """ data1 = pd.read_csv('tmdb_5000_movies.csv' ) data2 = pd.read_csv('tmdb_5000_credits.csv' ) data = data1.merge(data2,how='left' , left_on='id' , right_on='movie_id' )
这里合并完成之后可能会有重复的列,python会自动识别然后添加_x,_y这种的字段,比赛的时候要看清楚,这里可以自定义删除和重命名
1 2 3 4 data.rename({'title_x' :t1,'title_y' :t2},inplace=True ) del data['title_x' ]data.drop(columns=['title_x' ,'title_y' ])
合并完表之后可以重置索引
1 2 data = data.reset_index(drop=True )
做完这一部分之后可以保存一些防止后期用到
1 data.to_csv('movie.csv' )
任务2:数据处理(9分)
任务2-1(1分)
1 2 3 4 5 6 """ 删除指定字段,具体要求如下: • 删除homepage, original_title, overview, spoken_languages, status, tagline, movie_id字段 """ data.drop(columns=['homepage' , 'original_title' , 'overview' , 'spoken_languages' , 'status' , 'tagline' , 'movie_id' ],inplace=True )
任务2-2(1分)
1 2 3 4 5 """ 增加profit字段,该字段为每部电影的收益,具体要求如下: • 计算每部电影的收益,收益=电影票房-电影预算 """ data.loc[:,'profit' ] = data['revenue' ] - data['budget' ]
任务2-4(1分)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 """ 处理runtime字段的缺失值,该字段有两个缺失值,具体要求如下: • 将id为370980的缺失值填充为98 • 将id为459488的缺失值填充为81 """ data['runtime' ].dtype data['runtime' ] = data['runtime' ].astype(float ) data[data['runtime' ].isnull()] data[~data['runtime' ].isnull()] data.loc[(data['runtime' ].isnull()) & (data['id' ] == 370980 ),'runtime' ] = 98 data.loc[(data['runtime' ].isnull()) & (data['id' ] == 459488 ),'runtime' ] = 81
这里遇到些问题还没解决,就是对于缺失值处理我是用fillna
函数,并且在格式正确的情况下,inplace=True不生效,查了查百度是因为链式原因,多次调用了指定的id,那就麻烦点直接对原始数据进行替换。
如果填充的时候报错可能是索引的问题去重新搞一下索引
1 data.reset_index(drop=True )
任务2-5(2分)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 """ 分别对genres, keywords, production_companies, production_countries字段进行处理,具体要求如下: • 每个字段只保留该字段内容中name键值对应的value值,并以|分隔 提示:例如[{"id": 28, "name": "Action"}, {"id": 12, "name": "Adventure"}, {"id": 14, "name": "Fantasy"}, {"id": 878, "name": "Science Fiction"}],处理后变为Action|Adventure|Fantasy|Science Fiction """ import jsondef task2_5 (datas ): if isinstance (datas, str ): datas = json.loads(datas) names = [i['name' ] for i in datas] return '|' .join(names) data['genres' ] = data['genres' ].apply(task2_5) data['keywords' ] = data['keywords' ].apply(task2_5) data['production_companies' ] = data['production_companies' ].apply(task2_5) data['production_countries' ] = data['production_countries' ].apply(task2_5)
任务2-6(2分)
1 2 3 4 5 6 7 8 9 10 11 12 13 """ 对crew字段进行处理,具体要求如下: • 只保留导演的名字,并以|分隔 • 将crew字段列名改为director 提示:导演的job为Director """ def task2_6 (datas ): if isinstance (datas, str ): datas = json.loads(datas) names = [i['name' ] for i in datas if i['job' ] == 'Director' ] return '|' .join(names) data['crew' ] = data['crew' ].apply(task2_6) data.rename(columns={'crew' :'director' },inplace=True )
最后保存
1 data.to_csv('movie2.csv' )
任务3:数据可视化(20分)
任务3-1(2分)
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 """ 对电影的关键词制作词云图 • 成功制作词云图(1分) • 词云图图形为video(1分) """ from wordcloud import WordCloudfrom matplotlib import colorsfrom PIL import Imagekeywords_res = keywords.str .split('|' ).explode().unique() key_res = ' ' .join(keywords_res) video_mask = np.array(Image.open ('./img/map.jpg' )) color_list = ['black' ,'red' ,'orange' ] colormap = colors.ListedColormap(color_list) wordclound = WordCloud( mask = video_mask, background_color='white' , colormap=colormap ).generate(key_res) plt.figure(figsize=(10 ,5 )) plt.imshow(wordclound, interpolation='bilinear' ) plt.axis('off' ) plt.tight_layout() plt.savefig('wordcloud_map.png' ) plt.show()
词云图绘制要注意几个问题,最终genreate_from_text一定是以空格隔开的文本文字,如果比赛的时候提供了jieba包就可以
1 2 3 4 5 """ 这里text是一维的列表格式 """ cut = jieba.cut(text) string = ' ' .join(cut)
如果需要自动化保存n张图片可以使用随机数
1 2 3 4 ranInt = random.randint(1 ,1000000 ) plt.savefig(f'./static/cloud/{ranInt} .png' ) return f'./static/cloud/{ranInt} .png'
任务3-2(2分)
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 """ 制作电影的时长频率分布直方图 • 直方图图形类型正确(0.5分) • 直方图颜色为蓝色,透明度为 0.5,框线为黑色(0.5分) • 概率密度曲线以虚线表示(0.5分) • X轴和Y轴标签正确,左轴为直方图Y轴,右侧为概率密度曲线轴(0.5分) """ """ # 绘制直方图 n, bins, patches = plt.hist(runtimes, bins=20, color='skyblue', alpha=0.5, edgecolor='black', density=True) # 这里的density=True是显示频率,False是频数 # 计算组距 bin_width = bins[1] - bins[0] # 计算频率/组距 frequency = n / bin_width # 计算频率密度 total_count = len(runtimes) density = frequency / total_count # 计算频率密度 # freq_density = n / (len(runtimes) * bin_width) # 打印频率/组距和频率密度 print("频率/组距:", frequency) print("频率密度1:", density) print("频率密度2:", freq_density) """ runtimes = data['runtime' ] plt.figure(figsize=(10 , 6 ), dpi=120 ) plt.title('电影时长频率分布直方图' ) plt.xlabel('电影时长' ) plt.ylabel('频率/组距' ) n, bins, p = plt.hist(runtime,bins=32 , color='skyblue' , alpha=0.5 , density=True , edgecolor='black' ) ax2 = plt.twinx() ax2.set_ylabel('频率密度' ) ax2.plot(bins[:-1 ],n, linestyle='--' ) ax2.legend(['频率密度' ], loc='upper right' ) plt.show()
分化子图:
1 2 3 4 5 6 7 8 9 10 11 n, bins, patches = plt.hist(runtimes, bins=20 , color='blue' , alpha=0.5 , edgecolor='black' ) bin_width = bins[1 ] - bins[0 ] freq_density = n / (len (runtimes) * bin_width) fig, ax1 = plt.subplots() ax2 = ax1.twinx() ax2.plot(bins[:-1 ], freq_density, linestyle='--' , color='red' ) ax1.set_xlabel('电影时长(分钟)' ) ax1.set_ylabel('频率/组距' ) ax2.set_ylabel('频率密度' ) ax2.legend(['频率密度' ], loc='upper right' )
任务3-3(2分)
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 """ 制作电影产地分布玫瑰图 • 统计不同产地的电影数量,电影数量低于前五的产地,全部归为others • 成功制作玫瑰图(0.5分) • 标题为电影产地分布(0.5分) • 标签数值正确(0.5分) • 标签为国家:百分比,百分比保留两位小数,例如:Canada: 2.53%(0.5分) Canada: 2.53%,此数据为虚构的,真实数值需要自行计算 """ task3_3 = data['production_countries' ].str .split('|' ).apply(np.unique).explode() task3_3 = task3_3.value_counts() top5 = task3_3.head(5 ) other = task3_3[~task3_3.index.isin(top5.index)] other = pd.Series(other.sum (), index=['other' ]) task3_3_1 = top5.append(other) x = task3_3_1.index y = (task3_3_1 / task3_3_1.sum () * 100 ).round (2 ).values res = [[a,float (b)] for a,b in zip (x,y)] from pyecharts import optionsfrom pyecharts.charts import Piec = ( Pie() .add( series_name="" , data_pair=res, radius=['30%' ,'50%' ], start_angle=21 , rosetype=True , label_opts=options.LabelOpts(is_show=True ,formatter='{b}:{d}%' ) ) .set_global_opts( title_opts=options.TitleOpts(is_show=True , title='电影产地分布' ) ) ) c.render_notebook()
isin的反函数是~
,isin是一个非常好用的判断存在与否的函数,多用反正思维来做,比正向节省许多代码量
任务3-4(2分)
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 """ 统计票房均值Top10的导演,并以条形图展示 • 票房均值以整数表示,四舍五入 • 标题为票房排名Top10的导演(0.5分) • 标签数值正确(0.5分) • 标签位置放置右侧(0.5分) • 纵坐标导演名字显示完全(0.5分) """ task3_4 = data[['director' ,'revenue' ]].groupby('director' ).agg({'revenue' :'mean' }).astype('int64' ).sort_values(by='revenue' ).tail(10 ) x = task3_4.index y = task3_4.values.flatten() plt.figure(figsize=(10 , 6 ), dpi=120 ) plt.title('票房排名Top10的导演' , loc='left' ) plt.xlim(0 , 1500000000 ) plt.xticks(np.arange(0 , 1700000000 , 300000000 )) plt.gca().spines['right' ].set_visible(False ) plt.gca().spines['top' ].set_visible(False ) plt.barh(x, y, color='red' ) plt.show()
任务3-5(2分)
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 """ 分析原创电影与改编电影在收益、票房、预算方面的表现,以旋风图展示: • 成功制作旋风图(0.5分) • 旋风图标签数值正确,以万为单位,取整数(四舍五入)(0.5分) • 标题为原创电影与改编电影对比图(万)(0.5分) • 添加图例(0.5分) """ plt.rcParams['axes.unicode_minus' ] = False x1 = np.array([47 , 23 , 21 , 19 , 7 ]) x2 = np.array([17 , 33 , 11 , 2 , 17 ]) y = np.arange(5 ) labels=['' ,'特征1' ,'特征2' ,'特征三' ,'特征四' ,'特征五' ] fig = plt.figure(figsize=(12 ,8 )) ax1 = fig.add_subplot() ax1.barh(y, x1,0.8 ,color ='r' , label='改变' ) ax1.barh(y, -x2,0.8 ,color ='k' , label='远程' ) ax1.set_yticklabels(labels,fontsize=16 ) ax1.set_xlim(-60 ,60 ) ax1.set_xlabel('竞品分析' ,color='k' ,fontsize=16 ) plt.legend() for a,b in zip (x1,y): plt.text(a+5 , b, '%0.00f' % a, ha='center' , va= 'center' ,fontsize=16 ,color='r' ) for a,b in zip (x2,y): plt.text(-a-5 , b, '%0.00f' % a, ha='center' , va= 'center' ,fontsize=16 ,color='k' ) plt.show()
任务3-6(2分)
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 """ 统计各种电影类型所占的比例,以饼图展示 • 设置other类,当电影类型所占比例小于%1时,全部归到other类中(0.5分) • 所占比例小于或等于%2时,对应的饼状图往外突出一截(0.5分) • 数值正确,百分比保留一位小数(0.5分) • 标题为各种电影类型所占的比例(0.5分) """ task3_6 = data['genres' ].str .split('|' ).apply(np.unique).explode() task3_6 = task3_6[task3_6.apply(lambda x : len (x) > 0 )].reset_index(drop=True ).value_counts() task3_6_1 = (task3_6 / task3_6.sum () * 100 ) other = task3_6_1[task3_6_1 < 1 ].sum ().round (1 ) other = pd.Series(other, index=['other' ]) res = task3_6_1[task3_6_1 > 1 ].round (1 ).append(other) x = res.index y = res.values explode = [0.1 if i <= 2 else 0.02 for i in y] plt.figure(figsize=(10 ,6 ),dpi=120 ) plt.title('各种电影类型所占的比例' ) plt.pie(y,labels=x,explode=explode, autopct='%.1f%%' , startangle=50 ) plt.show()
任务3-7(2分)
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 """ 任务3-7(2分) 统计电影风格(类型)Top5随时间(1980年至今)的变化趋势,以折线图展示 • 折线图设置为顺滑模式(0.5分) • 标题和图例不能重叠(0.5分) • 数据起点和纵坐标没有间隙(0.5分) • 为最大值做标记(0.5分) """ task3_7 = data[['genres' ,'year' ]] task3_7 = task3_7[task3_7['genres' ].apply(lambda x:len (x) > 0 )].reset_index(drop=True ) task3_7['genres' ] = task3_7['genres' ].str .split('|' ).apply(np.unique) top5_index= task3_7['genres' ].explode().value_counts().nlargest(5 ).index task3_7_1 = task3_7.explode(column=['genres' ]) task3_7_1['number' ] = 1 task3_7_1 = task3_7_1[task3_7_1['genres' ].isin(top5_index)] task3_7_2 = task3_7_1.pivot_table(index=['year' ], columns=['genres' ],values=['number' ],aggfunc='sum' ) x = task3_7_2.index y1 = task3_7_2['number' ]['Action' ] y2 = task3_7_2['number' ]['Comedy' ] y3 = task3_7_2['number' ]['Drama' ] y4 = task3_7_2['number' ]['Romance' ] y5 = task3_7_2['number' ]['Thriller' ] y1.fillna(0 ,inplace=True ) y2.fillna(0 ,inplace=True ) y3.fillna(0 ,inplace=True ) y4.fillna(0 ,inplace=True ) y5.fillna(0 ,inplace=True ) max_id = y3.idxmax() max_value = int (y3.max ()) from scipy.interpolate import make_interp_splinex_smooth = np.linspace(int (x.min ()), int (x.max ()), 400 ) y1_smooth = make_interp_spline(x,y1)(x_smooth) y2_smooth = make_interp_spline(x,y2)(x_smooth) y3_smooth = make_interp_spline(x,y3)(x_smooth) y4_smooth = make_interp_spline(x,y4)(x_smooth) y5_smooth = make_interp_spline(x,y5)(x_smooth) plt.figure(figsize=(10 ,6 ),dpi=120 ) plt.title('电影风格(类型)Top5随时间(1980年至今)的变化趋势' ) plt.ylim(0 ,150 ) plt.yticks(np.arange(0 ,180 ,30 )) plt.xlim(1980 ,2017 ) plt.xticks(np.arange(1980 ,2018 ,2 )) plt.plot(x_smooth,y1_smooth) plt.plot(x_smooth,y2_smooth) plt.plot(x_smooth,y3_smooth) plt.plot(x_smooth,y4_smooth) plt.plot(x_smooth,y5_smooth) plt.text(max_id,max_value+5 ,max_value, va='bottom' , ha='center' ) plt.show()
任务3-8(2分)
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 """ 统计不同风格(类型)电影的收益能力,以条形图和折线图混合展示 • 条形图为不同电影类型的收益总和,以千亿为单位,保留两位小数(0.5分) • 折线图为不同电影类型的收益率,收益率=收益/预算(0.5分) • 条形图标签数值正确(0.5分) • 折线图不显示标签数值(0.5分) """ task3_8 = data[['genres' ,'profit' ,'budget' ]] task3_8 = task3_8[task3_8['genres' ].apply(lambda x : len (x) > 0 )].reset_index(drop=True ) task3_8['genres' ] = task3_8['genres' ].str .split('|' ) task3_8_1 = task3_8.explode(column='genres' ).groupby('genres' ).agg('sum' ) task3_8_1['pre' ] = (task3_8_1['profit' ] / task3_8_1['budget' ] * 100 ) task3_8_1['profit' ] = (task3_8_1['profit' ] / 100000000000 ).round (2 ) task3_8_1 = task3_8_1.sort_values(by='profit' ,ascending=False ) task3_8_1['profit' ] = task3_8_1['profit' ].apply(lambda x: 0 if (x == -0.00 ) else x) x = task3_8_1.index y1 = task3_8_1['profit' ].values y2 = task3_8_1['pre' ].values plt.figure(figsize=(10 ,6 ),dpi=120 ) plt.xticks(rotation=-45 , ha='left' ) plt.ylim(0 ,1.2 ) plt.yticks(np.arange(0 ,1.4 ,0.2 )) plt.bar(x,y1, color='red' ,label='收益' ) for a,b in zip (x,y1): plt.text(a,b+0.02 ,b,ha='center' ) plt.legend() ax2 = plt.twinx() ax2.set_ylim(-100 , 300 ) ax2.set_yticks(np.arange(-100 , 350 , 50 )) ax2.set_yticklabels([(str (i)+"%" ) for i in np.arange(-100 , 350 , 50 )]) ax2.set_ylabel('收益率' ) ax2.plot(x, y2, marker='o' , label='收益率' ) ax2.legend(loc='upper right' , bbox_to_anchor=(1 , 0.95 )) plt.grid(axis='y' ) plt.gca().spines['top' ].set_visible(False ) plt.show()
任务3-9(4分)
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 """ 统计电影类型Top5,以条形图展示 • 条形图颜色依次为#BCEE68, #EBBDBF, #D6A5DD, #A020F0, #76CBE8 (0.5分) • 条形图右端添加象形图片,symbols已提前给出,象形图框线为灰色grey (2分) • 标签数值正确(0.5分) • 标签放置条形图右侧,显示完全,不能被遮挡(1分) """ data2 = pd.read_csv('movie2.csv' ) data2['genres' ] = data2['genres' ].str .split('|' ) top5_genres = data2.explode('genres' ).groupby('genres' ).size().reset_index(name='number' ) top5_res = top5_genres.nlargest(5 ,columns='number' ) ''' genres number 6 Drama 2297 3 Comedy 1722 17 Thriller 1274 0 Action 1154 14 Romance 894 ''' genres = top5_res['genres' ] number = top5_res['number' ] colors = ['#BCEE68' , '#EBBDBF' , '#D6A5DD' , '#A020F0' , '#76CBE8' ] fig, ax = plt.subplots() ax.barh(genres, number, color=colors, edgecolor='grey' ) symbols = ['⚔️' , '🌍' , '🧙♂️' , '🚀' , '🐰' ] for a,b in zip (genres,number): ax.text(b, a, symbols[i], va='center' , ha='left' , fontsize=12 ) ax.set_xlabel('数量' ) ax.set_ylabel('电影类型' ) ax.set_title('电影类型Top5' ) ax.spines['right' ].set_visible(False ) ax.spines['top' ].set_visible(False ) ax.yaxis.set_ticks_position('left' ) ax.xaxis.set_ticks_position('bottom' ) plt.tight_layout() plt.show()
模块C:机器学习
四天把机器学习的几种算法过了一遍,难点在特征工程,对于模型的选择只需要明白是回归,分类还是聚类就好了,另外就是对于特征值和目标值的提取需要注意,由于没有数据练,只能找b站的几个数据练了
特征工程
特征提取
API
1 sklearn.feature_extraction
1、字典特征提取
需要先将数据转换成字典类型
1 2 x = x.to_dict(orient="recorda" )
需要注意的是调用转换器之后返回的是稀疏矩阵,这里sparse需要设置成false才不会返回sparse
1 2 3 4 5 6 7 8 9 10 11 12 13 from sklearn import feature_extraction as fedata = [{'city' :'bj' , 'tem' :100 }, {'city' :'sh' , 'tem' :300 }, {'city' :'sd' , 'tem' :50 }] transfer = fe.DictVectorizer(sparse=False ) data_new = transfer.fit_transform(data) data_new """ array([[ 1., 0., 0., 100.], [ 0., 0., 1., 300.], [ 0., 1., 0., 50.]]) """
2、文本特征提取
英文文本
1 2 3 4 5 6 7 8 9 10 11 12 from sklearn import feature_extraction as fedata = ["Lift is short, i like python" , "Lift is too long, i dislike sleep" ] transfer = fe.text.CountVectorizer() data_new = transfer.fit_transform(data) print (data_new.toarray())""" [[0 1 1 1 0 1 1 0 0] [1 1 1 0 1 0 0 1 1]] """
中文文本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import jiebadata = ['我爱北京天安门' , '天安门上太阳升' ] data_new = [] for i in data: data_new.append(" " .join(list (jieba.cut(i)))) data_new transfer = fe.text.CountVectorizer() data_final = transfer.fit_transform(data_new) data_final.toarray() """ array([[1, 1, 0], [0, 1, 1]], dtype=int64) """
3、TF-IDF
1 2 3 4 5 6 fe.text.TfidfVectorizer().fit_transform(data_new).toarray() """ array([[0.81480247, 0.57973867, 0. ], [0. , 0.57973867, 0.81480247]]) """
特征降维
降维是指在某些限定条件下,降低随机变量(特征)的个数,得到一组"不想关"主变量的过程大致就是我如果有三个特征,但是这三个特征之中有两个相关系数非常大,就选择这两者之一
降维之前要注意我所有的列是类似ont-hot一样的编码格式,不能出现英文或者中文
==降维方式 ==
特征选择
主成分分析(可以理解一种特征提取的方式)
1、特征选择
1 2 3 4 5 6 7 8 9 Filter过滤式 - 方差选择法:地方查特征过滤 - 相关系数 - 衡量特征与特征之间的相关性 Embeded嵌入式 - 决策树 - 正则化 - 深度学习
==过滤式 ==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import pandas as pdfrom sklearn.feature_selection import VarianceThresholddef variance_demo (): data = pd.read_csv(r"E:\Code\Pywork\Machine\data\factor_returns.csv" ) print ("data:\n" ,data) data = data.iloc[:, 1 :-2 ] print ("data:\n" ,data) transfer = VarianceThreshold(threshold=5 ) data_new = transfer.fit_transform(data) print ("data_new:\n" , data_new, data_new.shape) if __name__ == '__main__' : variance_demo()
==相关系数 ==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import pandas as pdfrom sklearn.feature_selection import VarianceThresholdfrom scipy.stats import pearsonrdef variance_demo (): data = pd.read_csv(r"E:\Code\Pywork\Machine\data\factor_returns.csv" ) data = data.iloc[:, 1 :-2 ] r = pearsonr(data['pe_ratio' ],data['pb_ratio' ]) print ("相关系数:" ,r) if __name__ == '__main__' : variance_demo()
2、主成分分析(PCA)
高维转低维
1 sklearn.decomposition.PCA
交叉表使用: table = pd.crosstab(data[‘user_id’],data[‘aisle’])
1 2 3 4 5 6 7 8 9 10 """array([[0.81480247, 0.57973867, 0. ], [0. , 0.57973867, 0.81480247]])""" from sklearn import decomposition as sdtransfer = sd.PCA(n_components=0.95 ) data_new = transfer.fit_transform(data2) print (data_new)""" [[ 0.57615236] [-0.57615236]] """
调优
网格搜索和交叉验证
API
1 2 from sklearn import model_selection as msms.GridSearchCV(model,param_grid=xxx,cv=xxx)
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 from sklearn import datasets as sdfrom sklearn import model_selection as msfrom sklearn import preprocessing as spfrom sklearn import neighbors as snfrom sklearn import metrics as smfrom sklearn.decomposition import PCAiris = sd.load_iris() transfer = PCA(n_components=0.95 ) x = iris.data y = iris.target x = transfer.fit_transform(x) train_x, test_x, train_y, test_y = ms.train_test_split(x, y, shuffle=True , random_state=5 , train_size=0.75 ) transfer = sp.StandardScaler() train_x = transfer.fit_transform(train_x) test_x = transfer.transform(test_x model = sn.KNeighborsClassifier() ms.GridSearchCV(model,param_grid={"n_neighbors" :{1 ,3 ,5 ,7 ,8 ,9 ,11 }},cv=20 ) model.fit(train_x, train_y) pred_y = model.predict(test_x) sm.f1_score(test_y, pred_y, average='micro' )
数据预处理
1、均值移除 (标准差标准化)
由于一个样本的不同特征值差异较大,不利于使用现有机器学习算法进行样本处理。
标准差标准化也叫零均值标准化或分数标准化,是当前使用最广泛的数据标准化方法。经过该方法处理的数据均值移除可以让样本矩阵中的每一列的平均值为0,标准差为1。
如何使样本矩阵中的每一列的平均值为0呢?
$$
X* = (X-mean)/ std
$$
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 import numpy as npimport pandas as pdimport sklearn.preprocessing as sparr01 = np.array([ [17 , 100 , 4000 ], [20 , 80 , 5000 ], [23 , 75 , 5500 ] ]) arr01 scaler = sp.StandardScaler() scaler.fit(arr01).transform(arr01) std_samples = scaler.fit_transform(arr01) """ array([[-1.22474487, 1.38873015, -1.33630621], [ 0. , -0.46291005, 0.26726124], [ 1.22474487, -0.9258201 , 1.06904497]]) """ scaler.inverse_transform(std_samples) sp.scale(arr01) array01_mean = arr01.mean(axis=0 ) arr01_new = (arr01-array01_mean)/arr01.std(axis=0 ) arr01_new.mean(axis=0 ) arr01_new.std(axis=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 class sklearn .preprocessing.StandardScaler(*, copy=True , with_mean=True , with_std=True )参数: with_mean:bool , default=True :如果为True ,则在缩放之前将数据居中。这在操作稀疏矩阵时不起作用(并且会引发一个异常),因为将它们居中需要构建一个密集矩阵,在常见的用例中,密集矩阵可能太大而无法容纳在内存中。 with_std:bool , default=True :如果为True ,则将数据缩放为单位方差(或等效的单位标准差)。 属性: scale_ :ndarray of shape (n_features,) or None :数据的每个特征相对缩放,以实现零均值和单位方差。通常使用np.sqrt(var)计算。如果方差为零,我们无法实现单位方差,数据保持原样,给出缩放因子1 。当with_std=False 时,scale_等于None 。 mean_ :ndarray of shape (n_features,) or None :训练集中每个特征的平均值。当_mean=False 时,等于None 。 var_ :ndarray of shape (n_features,) or None :训练集中每个特征的方差。用于计算scale_ 。当std=False 时,等于None 。 示例: >>> from sklearn.preprocessing import StandardScaler>>> data = [[0 , 0 ], [0 , 0 ], [1 , 1 ], [1 , 1 ]]>>> scaler = StandardScaler()>>> print (scaler.fit(data))StandardScaler() >>> print (scaler.mean_)[0.5 0.5 ] >>> print (scaler.transform(data))[[-1. -1. ] [-1. -1. ] [ 1. 1. ] [ 1. 1. ]] >>> print (scaler.transform([[2 , 2 ]]))[[3. 3. ]]
2、范围缩放(离差标准化)
另一种标准化方法是将特征缩放到给定的最小值和最大值之间,通常介于0和1之间,或者将每个特征的最大绝对值缩放到单位大小。这可以分别使用MinMaxScaler或MaxAbsScaler实现。使用这种缩放的动机包括对非常小的特征标准差的鲁棒性,以及在稀疏数据中保持零条目。
将样本矩阵中的每一列的最小值和最大值设定为相同的区间,统一各列特征值的范围。一般情况下会把特征值缩放至[0, 1]区间。
离差标准化是对原始数据的一种线性变换,结果是将原始数据的数值映射到[0,1]区间之间。
$$
X^*=(X-min)/(max-min)
$$
其中max为样本数据的最大值,min 为样本数据的最小值,max-min为极差。离差标准化保留了原始数据值之间的联系,是消除量纲和数据取值范围影响最简单的方法。
离差标准化的特点:
数据的整体分布情况并不会随离差标准化而发生改变,原先取值较大的数据,在做完离差标准化后的值依旧较大。
当数据和最小值相等的时候,通过离差标准化可以发现数据变为0。
同时,还可以看出离差标准化的缺点:若数据集中某个数值很大,则离差标准化的值就会接近于0,并且相互之间差别不大。若将来遇到超过目前属性[min,max]取值范围的时候,会引起系统出错,这时便需要重新确定min和max。
1 2 3 4 5 6 7 8 9 10 11 12 scaler = sp.MinMaxScaler() scaler.fit_transform(arr01) max_ = arr01.max (axis=0 ) min_ = arr01.min (axis=0 ) (arr01 - min_) / (max_ - min_) """ array([[0. , 1. , 0. ], [0.5 , 0.2 , 0.66666667], [1. , 0. , 1. ]]) """
3、异常值缩放数据
如果数据中包含了许多异常值,那么使用数据的均值和方差进行缩放可能不会很好地工作。在这些情况下,您可以使用RobustScaler作为替代品。它对数据的中心和范围使用了更可靠的估计。
使用对异常值具有鲁棒性的统计数据来缩放特征。
此缩放器删除中值,并根据分位数范围(默认为IQR:四分位数范围)缩放数据。IQR是第一个四分位数(第25分位数)和第三个四分位(第75分位数)之间的范围。通过计算训练集中样本的相关统计信息,对每个特征进行居中和缩放。然后存储中值和四分位间距,以便使用转换方法用于后续数据。
数据集的标准化是许多机器学习估计器的共同要求。通常,这是通过去除平均值并缩放到单位方差来实现的。然而,异常值通常会以负面方式影响样本均值/方差。在这种情况下,中值和四分位数范围通常会给出更好的结果。
1 2 3 4 5 6 7 scaler = sp.RobustScaler() scaler.fit_transform(arr01) """ array([[-1. , 1.6 , -1.33333333], [ 0. , 0. , 0. ], [ 1. , -0.4 , 0.66666667]]) """
4、归一化
归一化/标准化可以去除数据单位对计算带来的影响,也就是所谓的去量纲行为,归一化/标准化实质是一种线性变换,线性变换有很多良好的性质,这些性质决定了对数据改变后不会造成“失效”,反而能提高数据的表现,这些性质是归一化/标准化的前提。
归一化/标准化的去量纲作用能够带来以下两个好处:
1)提升模型的精度。一些分类器需要计算样本之间的距离(如欧氏距离),例如KNN。如果一个特征值域范围非常大,那么距离计算就主要取决于这个特征,从而与实际情况相悖(比如这时实际情况是值域范围小的特征更重要)。
2)提高收敛速度。对于线性模型来说,数据归一化/标准化后,最优解的寻优过程明显会变得平缓,更容易正确的收敛到最优解。
有些情况每个样本的每个特征值具体的值并不重要,但是每个样本特征值的占比更加重要。
动作
爱情
科幻
小明
20
10
5
小王
4
2
1
小李
15
11
13
所以归一化即是用每个样本的每个特征值除以该样本各个特征值绝对值的总和。变换后的样本矩阵,每个样本的特征值绝对值之和为1。
将样本单独归一化为单位范数。
1 2 3 4 5 6 7 8 9 10 11 12 API: class sklearn .preprocessing.Normalizer(norm='l2' , *, copy=True )参数: norm:{‘l1’, ‘l2’, ‘max ’}, default=’l2’:用于标准化每个非零样本的范数。如果使用norm='max' ,则值将按绝对值的最大值重新缩放。 这里: l1 是曼哈顿距离 l2 是欧几里得距离 copy:bool , default=True :设置为False 以执行在位行规范化并避免复制(如果输入已经是numpy数组或scipy.sparse CSR矩阵)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 scaler = sp.Normalizer(norm='l2' ) scaler.fit_transform(arr01) """ array([[0.00424863, 0.02499197, 0.99967862], [0.00399946, 0.01599782, 0.99986403], [0.00418139, 0.01363498, 0.9998983 ]]) """ scaler1 = sp.Normalizer(norm='l1' ) scaler1.fit_transform(arr01) """ array([[0.00412922, 0.02428953, 0.97158125], [0.00392157, 0.01568627, 0.98039216], [0.00410861, 0.01339764, 0.98249375]]) """
5、二值化
有些业务并不需要分析矩阵的详细完整数据(比如图像边缘识别只需要分析出图像边缘即可),可以根据一个事先给定的阈值,用0和1表示特征值不高于或高于阈值。二值化后的数组中每个元素非0即1,达到简化数学模型的目的。大于阈值的值映射到1,而小于或等于阈值的值则映射到0。默认阈值为0时,只有正值映射到1。
二值化是对文本计数数据的一种常见操作,分析人员可以决定只考虑特征的存在或不存在,而不是量化的出现次数。它还可以用作考虑布尔随机变量的估计器的预处理步骤(例如,在贝叶斯设置中使用伯努利分布建模)。
API:
class sklearn.preprocessing.Binarizer(*, threshold=0.0, copy=True)
参数:
threshold:float
, default=0.0:低于或等于此的特征值由0替换,高于此值由1替换。对于稀疏矩阵上的操作,阈值不能小于0。
1 2 3 4 5 6 7 8 scaler = sp.Binarizer(threshold=80 ) scaler.fit_transform(arr01) """ array([[0, 1, 1], [0, 0, 1], [0, 0, 1]]) """
6、独热编码(onehot
)
将分类特征编码为一个热点数字数组。为样本特征的每个值建立一个由一个1和若干个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 两个数 三个数 四个数 1 3 2 7 5 4 1 8 6 7 3 9 为每一个数字进行独热编码: 1 ->10 3 ->100 2 ->1000 7 ->01 5 ->0 10 4 ->0 1008 ->001 6 ->00 10 9 ->0001编码完毕后得到最终经过独热编码后的样本矩阵: 101001000 0 10100100100010010 011000001 API: class sklearn .preprocessing.OneHotEncoder(*, categories='auto' , drop=None , sparse=True , dtype=<class 'numpy.float64' >, handle_unknown='error' , min_frequency=None , max_categories=None )参数: categories:‘auto’ or a list of array-like, default=’auto’: “auto”:根据培训数据自动确定类别。 list :categories[i]包含第i列中预期的类别。传递的类别不应在单个功能中混合字符串和数值,并且应在数值的情况下进行排序。sparse:bool , default=True :如果设置为True ,将返回稀疏矩阵,否则将返回数组。 min_frequency:int or float , default=None :指定最低频率,低于该频率类别将被视为不常见。 max_categories:int , default=None :在考虑不常用类别时,为每个输入要素指定输出要素数量的上限。如果存在不常见的类别,max_categories将包括表示不常见类别的类别以及常见类别。如果无,则输出特征的数量没有限制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 >>> from sklearn.preprocessing import OneHotEncoder>>> enc = OneHotEncoder(handle_unknown='ignore' )>>> X = [['Male' , 1 ], ['Female' , 3 ], ['Female' , 2 ]]>>> enc.fit(X)OneHotEncoder(handle_unknown='ignore' ) >>> enc.categories_ [array(['Female' , 'Male' ], dtype=object ), array([1 , 2 , 3 ], dtype=object )] >>> enc.transform([['Female' , 1 ], ['Male' , 4 ]]).toarray() array([[1. , 0. , 1. , 0. , 0. ], [0. , 1. , 0. , 0. , 0. ]]) >>> enc.inverse_transform([[0 , 1 , 1 , 0 , 0 ], [0 , 0 , 0 , 1 , 0 ]]) array([['Male' , 1 ], [None , 2 ]], dtype=object ) >>> enc.get_feature_names_out(['gender' , 'group' ])array(['gender_Female' , 'gender_Male' , 'group_1' , 'group_2' , 'group_3' ], ...)
7、标签编码
对值介于0和n_classes-1之间的目标标签进行编码。根据字符串形式的特征值在特征序列中的位置,为其指定一个数字标签,用于提供给基于数值算法的学习模型。该转换器应用于编码目标值,即y,而不是输入X。
1 2 3 4 5 6 API: class sklearn .preprocessing.LabelEncoder()无参数 属性: classes_:ndarray of shape (n_classes,):保存每个类的标签。
1 2 3 4 5 6 7 8 9 10 11 12 13 >>> import numpy as np>>> import sklearn.preprocessing as sp>>> raw_samples = np.array([... 'audi' , 'ford' , 'audi' , 'toyota' ,... 'ford' , 'bmw' , 'toyota' , 'ford' ,... 'audi' ])>>> print (raw_samples)lbe = sp.LabelEncoder() lbe_samples = lbe.fit_transform(raw_samples) >>> print (lbe_samples)inv_samples = lbe.inverse_transform(lbe_samples) >>> print (inv_samples)
8、常用
类名
方法名
sklearn.preprocessing.Binarizer()
sklearn.preprocessing.binarize()
sklearn.preprocessing.LabelBinarizer()
sklearn.preprocessing.label_binarize()
sklearn.preprocessing.MaxAbsScaler()
sklearn.preprocessing.maxabs_scale()
sklearn.preprocessing.MinMaxScaler()
sklearn.preprocessing.minmax_scale()
sklearn.preprocessing.Normalizer()
sklearn.preprocessing.normalize()
sklearn.preprocessing.StandardScaler()
sklearn.preprocessing.scale()
sklearn.preprocessing.RobustScaler()
sklearn.preprocessing.robust_scale()
回归问题
回归问题是一种常见的监督机器学习任务,在很多领域均有广泛应用。其典型应用包括销量预测、库存预测、股票价格预测、天气预测等
大纲:
线性回归模型的目标函数(损失函数和正则函数)、线性回归模型的优化求解、回归任务的性能指标、线性回归模型的超参数调优以及使用sklearn实现线性回归模型的应用案例。
1、线性回归简介
回归分析:回归分析法指利用数据统计原理,对大量统计数据进行数学处理,并确定因变量y与某些自变量x的相关关系,建立一个相关性较好的回归方程(函数表达式),并加以外推,用于预测今后的因变量的变化分析方法。回归的目标是学习一个输入x到输出y的映射f,并根据该模型预测新的测试数据x对应的响应y=f(x)'公式:
$$
f(x,w)=w^T+b
$$
2、一元线性回归
可以看出来最后求的就是损失函数,获取最小值min 即距离线距离和最短
可以理解我每一段都需要获取最小值,
损失函数
损失函数就是总样本误差关于模型参数的函数,该函数属于三维数学模型,即需要找到一组w0
,w1
使得loss取极小值。
核心:找到w0和w1的值,使得预测值和真实值之间的平均差异最小。
损失:机器学习模型关于单个样本的预测值与真实值的差,损失越小,模型越好;如果预测值与真实值相等,就是没有损失。
损失函数:用于计算损失的函数模型每一次预测的好坏用损失函数来度量。
常见的损失函数:
l 平均平方误差(Mean Squared Error (MSE
)):也称为 L2
Loss,是机器学习、深度学习回归任务中最常用的一种损失函数,对离群点敏感。
l 平均绝对误差( Mean Absolute Error(MAE)):也称为L1
Loss,使用绝对值,L1
损失对离群点不敏感。
l 胡伯损失(Huber):综合了L2
损失和L1
损失的优点。
线性回归模型的优化求解
模型的目标函数确定后,我们就可以采用合适的优化方法寻找最佳的模型参数。在线性回归模型中,模型参数包括线性回归系数w1
,和截距w0
。当训练数据集不大时,最小二乘线性回归可采用解析求解法求解,解析求解法涉及到大量公式推导,此处暂不做讲解。除此以外还可以使用梯度下降法求解。
正规方程
直接求解W
举例:
1 2 3 4 5 y = ax^2 + bx +c y' = 2 ax + b = 0 x = - b / 2 a同理是矩阵求导= 0
梯度下降(Gradient Descent)
梯度下降法是求解无约束优化问题最常用的方法之一,亦被称为最速下降法。最小二乘回归和岭回归均可采用梯度下降法求解,Lasso回归由于目标函数中有L1正则函数而不可导,因此不能采用梯度下降法求解。梯度下降法是一种基于搜索的最优化方法,在机器学习中,熟练的使用梯度法(下降法或上升法)求取目标函数的最优解是非常重要的。线性回归算法模型的本质就是最小化一个损失函数,然后求出损失函数的参数的数学解; 梯度下降法是在机器学习领域中最小化损失函数的最为常用的方法。
假如你迷失在山上的浓雾之中,完全看不见下山的方向,你能感觉到的只有脚下的路面坡度。快速到达山脚的一个策略就是沿着最陡的方向下坡。这就是梯度下降的做法:通过测量参数向量θ相关的损失函数的局部梯度,并不断沿着降低梯度的方向调整,直到梯度将为0,达到最小值! 每下降一步都去选择最陡峭的方向,然后踏出一步。因此没迭代一次需要考虑两个变量,一个是方向(朝哪边走),一个是步长(走多少)。方向就是向量θ的斜率,步长是一个超参数叫做学习率(learning_rate)。
==学习速率(learning_rate) ==
学习率是一个超参数,学习率的取值会影响获得最优解的速度; 太小,算法需要经过大量迭代才能收敛,这将耗费很长时间;反过来学习率太大,可能会越过最小值直接到达另一边,甚至有可能比之前的起点还要高,这会导致算法发散,值越来越大,无法找到最优解。学习率是超参数需要手动调节,取值范围一般在[0, 1]之间。下图展示了不同学习率对梯度下降的影响。
3、回归相关API
sklearn.linear_model
模块下提供了线性分类器(Linear classifiers)和经典线性回归(Classical linear regressors)两种问题的实现类。其中Linear classifiers中的类是用线性回归的思想解决分类问题的,其中包含了逻辑回归分类器(LogisticRegression
),岭回归分类器(RidgeClassifier
),交叉验证逻辑回归分类器(RidgeClassifierCV
),线性感知器分类器(Perceptron
),SGD
训练的线性分类器(SGDClassifier
)等多种分类器。Classical linear regressors模块中提供了线性回归(LinearRegression
),岭回归(Ridge),交叉验证岭回归(RidgeCV
),随机梯度下降回归(SGDRegressor
)。除此以外还有带有变量选择的回归器(Regressors with variable selection);贝叶斯回归(Bayesian regressors);具有变量选择的多任务线性回归器(Multi-task linear regressors with variable selection);离群稳健回归(Outlier-robust regressors
);回归的广义线性模型(Generalized linear models (GLM
) for regression);混合模型(Miscellaneous)等。
==1.LinearRegression()
==
1 2 3 4 5 """ 普通最小二乘线性回归。线性回归拟合系数w=(w1,…,wp)的线性模型,以最小化数据集中观察到的目标与线性近似预测的目标之间的残差平方和。 """ sklearn.linear_model.LinearRegression(*, fit_intercept=True , normalize=False , copy_X=True , n_jobs=None )
参数名称
描述
fit_intercept
是否计算此模型的截距。如果设置为False,则不会在计算中使用截距(即,预计数据将居中)。
normalize
当fit_intercept设置为False时,将忽略此参数。如果为True,则回归前将通过减去平均值并除以l2范数对回归X进行归一化。如果您希望标准化,请在对normalize=False的估计器调用fit之前使用StandardScaler。
copy_X
如果为True,将复制X;否则,可能会被覆盖。
n_jobs
用于计算的作业数
属性
描述
coef_
系数。形状为(n_features,)或(n_targets,n_features)的数组
rank_
矩阵的等级X
intercept_
截距
方法
描述
fit(X, y)
拟合线性模型
get_params([deep])
获取此估计器的参数。
predict(X)
使用线性模型进行预测。
score(X, y)
返回预测的确定系数。
set_params()
设置此估计器的参数。
1 2 3 4 5 6 7 8 9 10 11 12 示例代码: import sklearn.linear_model as lmmodel = lm.LinearRegression() model.fit(输入, 输出) result = model.predict(array)
==2.SGDRegressor()
==
1 2 3 4 5 6 """ 通过SGD最小化正则化经验损失拟合的线性模型。SGD代表随机梯度下降:一次估计每个样本的损失梯度,并以递减的强度计划(即学习率)更新模型。正则化器是添加到损失函数的惩罚,其使用平方欧几里德范数L2或绝对范数L1或两者的组合(弹性网)将模型参数收缩到零向量。如果参数更新由于正则化器而与0.0值交叉,则更新将被截断为0.0,以允许学习稀疏模型并实现在线特征选择。此实现可以处理表示为特征浮点值密集numpy数组的数据。 """ class sklearn .linear_model.SGDRegressor(loss='squared_error' , *, penalty='l2' , alpha=0.0001 , l1_ratio=0.15 , fit_intercept=True , max_iter=1000 , tol=0.001 , shuffle=True , verbose=0 , epsilon=0.1 , random_state=None , learning_rate='invscaling' , eta0=0.01 , power_t=0.25 , early_stopping=False , validation_fraction=0.1 , n_iter_no_change=5 , warm_start=False , average=False )
参数
说明
loss
损失函数,str,默认值为:squared_error,可选:‘squared_error’, ‘huber’, ‘epsilon_insensitive’, or ‘squared_epsilon_insensitive’
penalty
正则项,可选:{‘l2’, ‘l1’, ‘elasticnet’}, 默认值为’l2’
alpha
正则强度,浮点型,默认值为:0.0001。值越大,正则化越强。
fit_intercept
是否计算截距,布尔型,默认值为:True。是否应估计截距。如果为False,则假定数据已居中。
learning_rate
学习率,字符串,默认值为:invscaling,不按比例缩放。
eta0
学习率的初始值,浮点型, ‘constant’, ‘invscaling’ or ‘adaptive’初始学习率,默认为0.01
属性
说明
coef_
指定要素的权重斜率。
intercept_
截距项。
n_iter_
达到停止标准之前的实际迭代次数。
t_
训练期间执行的体重更新次数
n_features_in_
装配过程中看到的特征数量。
feature_names_in_
装配过程中看到的特征名称。仅当X具有全部字符串的要素名称时才定义。
方法
说明
fit(X, y)
用随机梯度下降拟合线性模型。
predict(X)
使用模型预测
get_params(deep=True)
获取此估计器的参数。
sparsify()
将系数矩阵转换为稀疏格式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 >>> import numpy as np>>> from sklearn.linear_model import SGDRegressor>>> from sklearn.pipeline import make_pipeline>>> from sklearn.preprocessing import StandardScaler>>> n_samples, n_features = 10 , 5 >>> rng = np.random.RandomState(0 )>>> y = rng.randn(n_samples)>>> X = rng.randn(n_samples, n_features)>>> >>> reg = make_pipeline(StandardScaler(),... SGDRegressor(max_iter=1000 , tol=1e-3 ))>>> reg.fit(X, y)Pipeline(steps=[('standardscaler' , StandardScaler()), ('sgdregressor' , SGDRegressor())])
4、案例实战:加利福尼亚房屋价格回归分析
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 import numpy as npimport pandas as pdfrom sklearn import datasets as sdfrom sklearn import model_selection as smfrom sklearn import linear_model as sldata = sd.fetch_california_housing() x = data['data' ] y = data['target' ] ''' 查看形状 x.shape y.shape 数据介绍 print(data['DESCR']) ''' train_x, test_x, train_y, test_y = sm.train_test_split(x, y, train_size=0.75 , test_size=0.25 , shuffle=True , random_state=145 ) """ 这里: train_size + test_size = 1 shuffle = True 混洗 random_state = 145 随机数种子 """ model = sl.LinearRegression() model.fit(train_x, train_y) pred_y = model.predict(test_x) """ 预测这里主要看的就是看预测值与测试值之间的区别 预测值: array([0.0696445 , 0.61678432, 2.02419006, ..., 1.9095006 , 2.67847098, 2.07215543]) 测试值: array([0.988, 1.572, 1.609, ..., 1.361, 2.727, 3.167]) """ sm.mean_squared_error(test_y, pred_y)
从上面可以看出来随机数种子的选择和对测试集和训练集的划分对最后模型的评估有着很重要的作用
5、回归模型评价指标
sklearn度量模块实现了几个损失、分数和效用函数来度量回归性能。其中一些已被增强以处理多输出情况:mean_squared_error、mean_absolute_error,r2_score,explained_variance_score、mean_pinball_loss、d2_pinball_score和d2_absolute _error_score。
方法名称
最优值
sklearn函数
平均绝对误差
0.0
metrics. mean_absolute_error(y_true, y_pred)
平均均方误差
0.0
metrics. mean_squared_error(y_true, y_pred)
均方对数误差
0.0
metrics.mean_squared_log_error(y_true, y_pred)
中位绝对误差
0.0
metrics. median_absolute_error(y_true, y_pred)
最大误差
0.0
metrics.max_error(y_true, y_pred)
可解释方差值
1.0
metrics. explained_variance_score(y_true, y_pred)
R方值
1.0
metrics. r2_score(y_true, y_pred)
示例:
线性回归模型训练完毕后,可以利用测试集评估训练结果误差。sklearn.metrics提供了计算模型误差的几个常用算法:
1 2 3 4 5 6 7 8 9 10 11 12 import sklearn.metrics as sm sm.mean_absolute_error(test_y, pred_y) sm.mean_squared_error(test_y, pred_y) sm.median_absolute_error(test_y, pred_y) sm.explained_variance_score(test_y, y_pred) sm.r2_score(test_y, pred_y)
6、过拟合和欠拟合
欠拟合
原因: 学习导数据的特征过少
解决办法: 增加数据的特征数量
过于简单的模型,无论对于训练数据还是测试数据都无法给出足够高的预测精度,这种现象叫做欠拟合。
过拟合
原因: 原始特征过多,存在一些嘈杂特征,模型过于复杂是因为模型查实去兼顾各个测试数据点
解决办法: 正则化
过于复杂的模型,对于训练数据可以得到较高的预测精度,但对于测试数据通常精度较低,这种现象叫做过拟合。
一个性能可以接受的学习模型应该对训练数据和测试数据都有接近的预测精度,而且精度不能太低。
1 2 3 4 训练集R2 测试集R2 0.3 0.4 欠拟合:过于简单,无法反映数据的规则 0.9 0.2 过拟合:过于复杂,太特殊,缺乏一般性 0.7 0.6 可接受:复杂度适中,既反映数据的规则,同时又不失一般性
7、多元线性回归
在回归分析中,如果有两个或两个以上的自变量,就称为多元回归。
例如房屋价格预测,其中房屋价格会受到房屋面积,卧室数量,楼层,房屋年限等多种因素的影响,每个自变量对价格(因变量)的影响程度不一样
8、多项式回归
如果数据比直线更复杂怎么办?可以使用线性模型来拟合非线性数据。一个简单的方法就是将每个特征的幂次方添加为一个新特征,然后在此扩展特征集上训练一个线性模型。这种技术称为多项式回归。
9、正则化线性模型
减少过拟合的一个好方法是对模型进行正则化(即约束模型):它拥有的自由度越少,则过拟合数据的难度就越大。
多项式模型正则化的一种简单方法是减少多项式的次数。
线性模型正则化通常是通过约束模型的权重来实现的。
岭回归、Lasso回归和弹性网络,它们实现了三种限制权重的方法
我的理解就是数据种有脏数据,有异常值即过大或过小,而正则化就是排除这些异常
各个范式的理解:
https://blog.csdn.net/vincent_duan/article/details/117305250
==岭回归 ==
岭回归也称为Tikhonov正则化,是线性回归的正则化版本。普通线性回归模型使用基于梯度下降的最小二乘法,在最小化损失函数的前提下,寻找最优模型参数,于此过程中,包括少数异常样本 在内的全部训练数据都会对最终模型参数造成程度相等的影响,异常值对模型所带来影响无法在训练过程中 被识别出来。为此,岭回归在模型迭代过程所依据的损失函数中增加了正则项 ,以限制模型参数对异常样本的匹配程度,进而提高模型面对多数正常样本的拟合精度。
线性回归+L2正则项:Ridge 回归(岭回归)
超参数γ控制要对模型进行正则化的程度。如果γ=0,则岭回归仅是线性回归。如果γ非常大,则所有权重最终都非常接近于零,结果是一条经过数据均值的平线。
在执行岭回归之前缩放数据(例如使用StandardScaler)很重要,因为它对输入特征的缩放敏感
1 2 class sklearn .linear_model.Ridge(alpha=1.0 , *, fit_intercept=True , copy_X=True , max_iter=None , tol=0.0001 , solver='auto' , positive=False , random_state=None , normalize=False )
normalize设置为True相当于先进行了一次标准化
alpha是正则化力度,取值: 0-1 1-10
solver:会根据数据自动选择优化方法
sag:如果数据集、特征都比较大,选择该随机梯度下降优化
==Lasso 回归 ==
线性回归+L1正则项:Lasso 回归(套索回归)
与岭回归一样,它也是向成本函数添加一个正则项,但是它增加的是权重向量的L1范数,而不是L2范数
1 2 3 rom sklearn import linear_model linear_model.Lasso(alpha=1.0 , *, fit_intercept=True , normalize=False , precompute=False , copy_X=True , max_iter=1000 , tol=0.0001 , warm_start=False , positive=False , random_state=None , selection='cyclic' )
==弹性网络 ==
弹性网络是介于岭回归和Lasso回归之间的中间地带。正则项是岭和Lasso正则项的简单混合,可以控制混合比ρ。
当ρ=0时,弹性网络等效于岭回归;
当ρ=1时,弹性网络等于Lasso回归;
那么什么时候使用普通的线性回归岭、Lasso或弹性网络呢?通常,有正则化,哪怕很小,总比没有更可取一些,所以大部分情况下,应该避免使用纯线性回归。岭回归是个不错的默认选择,但是如果实际用到的特征只有少数几个,那就应该更倾向于Lasso回归或者弹性网络,因为它们会将无用特征的权重降为零。一般,弹性网络优于Lasso回归,因为当特征数量超过训练实例数量,又或者是几个特征强相关时,Lasso回归的表现可能非常不稳定。
1 2 3 from sklearn.linear_model import ElasticNetElasticNet(alpha=1.0 , *, l1_ratio=0.5 , fit_intercept=True , normalize=False , precompute=False , max_iter=1000 , copy_X=True , tol=0.0001 , warm_start=False , positive=False , random_state=None , selection='cyclic' )
逻辑回归
逻辑回归分类模型是一种基于回归思想实现分类业务的分类模型。
逻辑回归的工作原理:与线性回归模型一样,逻辑回归模型也是计算输入特征的加权和(加上偏置项),但是不同于线性回归模型直接输出结果,它输出的是结果的数理逻辑。分类问题可以分为二元分类(OvO:one-versus-all)和多元分类(OvR:one-versus-all)。
逻辑回归做二元分类时的核心思想为:
针对输出为{0, 1}的已知训练样本训练一个回归模型,使得训练样本的预测输出限制在(0, 1)的数值区间。该模型使原类别为0的样本的输出更接近于0,原类别为1的样本的输出更接近于1。这样就可以使用相同的回归模型来完成分类预测。
1、逻辑回归原理
逻辑回归目标函数:
$$
逻辑函数(sigmoid):y=1/(1+e^(-z) ); z=w^T x+b
$$
1 2 3 4 5 6 7 8 9 import sklearn.linear_model as lm model = lm.LogisticRegression(solver='liblinear' , C=正则强度) model.fit(训练输入集,训练输出集) result = model.predict(带预测输入集)
2、交叉验证
在机器学习中,因为训练集和测试集的数据划分是随机的,所以有时会重复地使用数据,以便更好地评估模型的有效性,并选出最好的模型,该做法称为交叉验证。具体而言就是对原始样本数据进行切分,然后组合成为多组不同的训练集和测试集,用训练集训练模型,用测试集评估模型。某次的训练集可能是下次的测试集,故而称为交叉验证。
交叉验证的方法有简单交叉验证、K折交叉验证和留一交叉验证3种。其中K折交叉验证应用较为广泛,它是指将数据集随机等分为K份,每次选取K-1份作为训练集,用剩下的1份作为测试集,得到K个模型后将这K个模型的平均测试效果作为最终的模型效果。
通常来说,如果训练集相对较小,则增大K值,这样在每次迭代过程中将会有更多数据用于模型训练,同时算法时间延长;如果训练集相对较大,则减小K值,这样可以降低模型在不同的数据块上进行重复拟合性能评估的计算成本,在平均性能的基础上获得模型的准确评估。
除了更精确地评估模型,交叉验证的另一个重要作用就是利用更精确的评估结果对模型进行参数调优,它经常与GridSearch
网格搜索配合使用。
就是把将训练集分为不同的折,然后将测试集交叉放进去与训练集拟合,好处就是可以学到训练集的细节
1 2 3 4 import sklearn.model_selection as ms指标值数组 = ms.cross_val_score(模型, 输入集, 输出集, cv=折叠数, scoring=指标名)
3、数据集划分
1 2 3 4 5 数据集划分相关API: import sklearn.model_selection as ms X_train, X_test, y_train, y_test = ms.train_test_split(X, y, test_size=测试集占比, random_state=随机种子)
参数名称
说明
*arrays
接收一个或多个数据集。代表需要划分的数据集,若为分类回归则分别传入数据和标签,若为聚类则传入数 据。无默认。
test_size
接收float,int,None类型的数据。代表测试集的大小。如果传入的为float类型的数据则需要限定在0-1之 间,代表测试集在总数中的占比;如果传入为int类型的数据,则表示测试集记录的绝对数目。该参数与 train_size可以只传入一个。在0.21版本前,若test_size和train_size均为默认则testsize为25%。
train_size
接收float,int,None类型的数据。代表训练集的大小。该参数与test_size可以只传入一个。
random_state
接收int。代表随机种子编号,相同随机种子编号产生相同的随机结果,不同的随机种子编号产生不同的随机结果。默认为None。random_state就是为了保证程序每次运行都分割一样的训练集合测试集。
shuffle
接收boolean。代表是否进行有放回抽样。若该参数取值为True则stratify参数必须不能为空。
stratify
接收array或者None。如果不为None,则使用传入的标签进行分层抽样。
train_test_split是最常用的数据划分方法,在model_selection模块中还提供了其他数据集划分的函数,如PredefinedSplit
,ShuffleSplit
等。
划分训练集和测试集在某种程度上也是为了检查模型是否出现过拟合。过拟合指模型在训练样本中拟合程度过高,虽然它很好地契合了训练集数据,但是却丧失了泛化能力,因而不具有推广性,导致在测试集数据中的预测表现不佳。就好比每次模考都做同一份卷子,训练时得分很高,但是期末考试换了一套卷子就得分很低。而划分训练集和测试集可以用来对模型进行更好的检验。
4、模型搭建
1 2 3 4 5 6 7 8 9 import sklearn.linear_model as lm model = lm.LogisticRegression(solver='liblinear' , C=正则强度) model.fit(X_train,y_train)
1 2 3 4 5 """ 如果这里solver不选择liblinear 会默认选择 lbfgs 官网这样说的:The ‘newton-cg’, ‘sag’, and ‘lbfgs’ solvers support only L2 regularization with primal formulation, or no regularization. The ‘liblinear’ solver supports both L1 and L2 regularization, with a dual formulation only for the L2 penalty. The Elastic-Net regularization is only supported by the ‘saga’ solver. """
5、分类问题模型评估
==一级指标 ==
分类模型的评价指标:真假与正类负类
a. 真正例(TP) 是指模型将正类别样本正确地预测为正类别。
b. 真负例(TN) 是指模型将负类别样本正确地预测为负类别。
c. 假正例(FP) 是指模型将负类别样本错误地预测为正类别。
d. 假负例(FN) 是指将正类别样本错误地预测为负类别。
混淆矩阵
混淆矩阵(也称误差矩阵,Confusion Matrix)就是分别统计分类模型归错类,归对类的观测值个数,然后把结果放在一个表里展示出来。这个表就是混淆矩阵。
真实值
真实值
Positive
Negative
预测值
Positive
真正例(TP)
假正例(FP)
预测值
Negative
假负例(FN)
真负例(TN)
1 2 3 4 import sklearn.metrics as sm混淆矩阵 = sm.confusion_matrix(实际输出, 预测输出)
==二级指标 ==
准确率(accuracy):是指模型预测正确的样本数比上总样本数的比重。
计算公式:accuracy = (TP + TN) / (TP + TN + FP + FN)
精确率(precision_weighted):针对每一个类别,预测正确的样本数比上预测出来的样本数。
计算公式:precision_weighted = TP / TP + FP
召回率(recall_weighted):针对每一个类别,预测正确的样本数比上实际存在的样本数。
计算公式:recall_weighted = TP / TP + FN
某池塘有1400条鲤鱼,300只虾,300只鳖。现在以捕鲤鱼为目的。 撒一大网,逮着了700条鲤鱼,200只虾,100只鳖。那么,这些指标 分别如下:
精确率 = 700 / (700 + 200 + 100) = 70%
召回率 = 700 / 1400 = 50%
F值 = 70% * 50% * 2 / (70% + 50%) = 58.3%
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 sm.accuracy_score(test_y, pred_y) sm.precision_score(test_y, pred_y) ''' 这里会报错: ValueError: Target is multiclass but average='binary'. Please choose another average setting, one of [None, 'micro', 'macro', 'weighted']. 只需要更改: sm.precision_score(test_y, pred_y, average=None) ''' sm.precision_score(test_y, pred_y, average=None ) ''' array([0.8 , 0.94444444, 1. ]) ''' sm.precision_score(test_y, pred_y, average='macro' ) ''' 0.9148148148148149 ''' sm.recall_score(test_y, pred_y, average=None ) ''' array([0.88888889, 0.89473684, 1. ]) ''' sm.recall_score(test_y, pred_y, average='macro' ) ''' 0.9278752436647174 '''
精确率和召回率有一定关系 ->
精确率是预测正确的比上预测出来的
召回率是预测正确的比上原来就有的
记住一个结论:精确率和召回率是此消彼长的关系
例如:抖音审核,精确率上升,召回率下降
==三级指标 ==
• F1_Score(f1_weighted):2x精确率x召回率/(精确率+召回率)
分类模型对测试集进行预测而得出的准确率(accuracy)并不能很好地反映模型的性能,为了有效判断一个预测模型的性能表现,需要结合真实值,计算出精确率(precision_weighted)、召回率(recall_weighted)、F1_Score和Cohen’s Kappa系数等指标来衡量。分值越高越好。
要全面评估模型的有效性,必须同时 检查精确率(precision_weighted)和召回率(recall_weighted)。遗憾的是,精确率和召回率往往是此消彼长的情况。也就是说,提高精确率通常会降低召回率值,反之亦然。提高分类阈值,精确率可能会提高(因为FP可能会减小);召回率会下降或保持不变(因为TP会减少或不变,且FN会增加或不变)。降低分类阈值,精确率可能会下降(因为FP可能会增加),而召回率(FN可能会减少)可能会有所提高。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 sm.f1_score(test_y, pred_y, average='macro' ) ''' 0.9203413940256047 ''' print (sm.classification_report(test_y, pred_y))""" precision recall f1-score support 0 0.80 0.89 0.84 9 1 0.94 0.89 0.92 19 2 1.00 1.00 1.00 17 accuracy 0.93 45 macro avg 0.91 0.93 0.92 45 weighted avg 0.94 0.93 0.93 45 """
6、红酒分类
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 import numpy as npimport pandas as pdfrom sklearn import datasets as sdfrom sklearn import model_selection as msfrom sklearn import linear_model as lmfrom sklearn import metrics as smdata = sd.load_wine() print (data['DESCR' ])""" ============================= ==== ===== ======= ===== Min Max Mean SD ============================= ==== ===== ======= ===== Alcohol: 11.0 14.8 13.0 0.8 Malic Acid: 0.74 5.80 2.34 1.12 Ash: 1.36 3.23 2.36 0.27 Alcalinity of Ash: 10.6 30.0 19.5 3.3 Magnesium: 70.0 162.0 99.7 14.3 Total Phenols: 0.98 3.88 2.29 0.63 Flavanoids: 0.34 5.08 2.03 1.00 Nonflavanoid Phenols: 0.13 0.66 0.36 0.12 Proanthocyanins: 0.41 3.58 1.59 0.57 Colour Intensity: 1.3 13.0 5.1 2.3 Hue: 0.48 1.71 0.96 0.23 OD280/OD315 of diluted wines: 1.27 4.00 2.61 0.71 Proline: 278 1680 746 315 ============================= ==== ===== ======= ===== """ x = data['data' ] y = data['target' ] train_x, test_x, train_y, test_y = ms.train_test_split(x, y, train_size=0.75 , shuffle=True , random_state=7 ) model = lm.LogisticRegression(solver='liblinear' ) ''' 这里solver切记要切换模型 不然下面训练模型会出错 ''' model.fit(train_x, train_y) pred_y = model.predict(test_x) ''' array([2, 0, 2, 2, 1, 2, 1, 0, 1, 2, 0, 1, 2, 1, 1, 1, 1, 2, 0, 0, 0, 1, 1, 1, 0, 2, 1, 2, 2, 2, 1, 1, 2, 1, 1, 1, 2, 2, 0, 2, 0, 0, 2, 2, 1]) ''' ''' array([2, 0, 2, 2, 1, 2, 1, 0, 1, 2, 0, 1, 2, 1, 1, 1, 1, 2, 0, 0, 1, 1, 1, 1, 0, 2, 1, 2, 2, 2, 1, 0, 2, 1, 1, 1, 2, 2, 0, 2, 0, 1, 2, 2, 1]) ''' sm.confusion_matrix(test_y, pred_y) ''' array([[ 8, 1, 0], [ 2, 17, 0], [ 0, 0, 17]], dtype=int64) 这里横轴相加为真实值的各个类的总和 纵轴相加为预测值的各个类的总和 ''' sm.accuracy_score(test_y, pred_y) sm.precision_score(test_y, pred_y, average='macro' ) ''' 0.9148148148148149 ''' sm.recall_score(test_y, pred_y, average=None ) ''' array([0.88888889, 0.89473684, 1. ]) ''' sm.recall_score(test_y, pred_y, average='macro' ) ''' 0.9278752436647174 ''' sm.f1_score(test_y, pred_y, average='macro' ) ''' 0.9203413940256047 ''' print (sm.classification_report(test_y, pred_y))""" precision recall f1-score support 0 0.80 0.89 0.84 9 1 0.94 0.89 0.92 19 2 1.00 1.00 1.00 17 accuracy 0.93 45 macro avg 0.91 0.93 0.92 45 weighted avg 0.94 0.93 0.93 45 """
决策树
**决策树模型的几个重要概念:**父节点和子节点、根节点和叶子节点。
父节点和子节点是相对的,子节点由父节点根据某一规则分裂而来,然后子节点作为新的父节点继续分裂,直至不能分裂为止。
根节点则和叶子节点是绝对的,根节点是没有父节点的节点,即初始节点,叶子节点则是没有子节点的节点,即最终节点。
**决策树模型的关键:**就是如何选择合适的节点进行分裂。
1、决策树回归器模型相关API
1 2 3 4 5 6 7 import sklearn.tree as st sklearn.tree.DecisionTreeClassifier(*, criterion='gini' , splitter='best' , max_depth=None , min_samples_split=2 , min_samples_leaf=1 , min_weight_fraction_leaf=0.0 , max_features=None , random_state=None , max_leaf_nodes=None , min_impurity_decrease=0.0 , min_impurity_split=None , class_weight=None , ccp_alpha=0.0 ) sklearn.tree.DecisionTreeRegressor(*, criterion='mse' , splitter='best' , max_depth=None , min_samples_split=2 , min_samples_leaf=1 , min_weight_fraction_leaf=0.0 , max_features=None , random_state=None , max_leaf_nodes=None , min_impurity_decrease=0.0 , min_impurity_split=None , ccp_alpha=0.0 )
参数
含义
criterion:{“gini”, “entropy”}, default=”gini”
衡量分类的质量“entropy”代表信息增益,"gini"代表基尼系数
max_depth
树的最大深度
min_samples_split
一个内部节点需要的最少的样本数
min_samples_leaf
一个叶节点所需要的最小样本数
max_leaf_nodes
最大叶子节点的值
class_weight
类的关联权值
2、鸢尾花回归决策树
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 import pandas as pdimport numpy as npfrom sklearn import datasets as sdfrom sklearn import model_selection as msfrom sklearn import tree as stfrom sklearn import metrics as smdata = sd.load_iris() x = data.data y = data.target train_x, test_x, train_y, test_y = ms.train_test_split(x, y, train_size=0.75 , shuffle=True , random_state=7 ) model = st.DecisionTreeClassifier() model.fit(train_x, train_y) pred_y = model.predict(test_x) sm.f1_score(test_y, pred_y, average='micro' ) st.plot_tree(model,feature_names=['sepal length' , 'sepal width' , 'petal length' , 'petal width' ]) model.feature_importances_ ''' array([0.01786711, 0. , 0.0329131 , 0.94921978]) 所以我们选择data的时候只需要后面两列就ok '''
集合算法
集合算法也叫集成学习模型,它是使用一系列弱学习器(也称为基础模型或基模型)进行学习,并将各个弱学习器的结果进行整合,从而获得比单个学习器更好的学习效果。集成学习模型的常见算法有Bagging算法和Boosting算法两种。Bagging算法的典型机器学习模型为随机森林模型,而Boosting算法的典型机器学习模型为:AdaBoost、GBDT、XGBoost和LightGBM模型。
Bagging算法
Bagging算法的原理类似投票,每个弱学习器都有一票,最终根据所有弱学习器的投票,按照“少数服从多数”的原则产生最终的预测结果。
假设原始数据共有10000条,从中随机有放回地抽取10000次数据构成一个新的训练集(因为是随机有放回抽样,所以可能出现某一条数据多次被抽中,也有可能某一条数据一次也没有被抽中),每次使用一个训练集训练一个弱学习器。这样有放回地随机抽取n次后,训练结束时就能获得由不同的训练集训练出的n个弱学习器,根据这n个弱学习器的预测结果,按照“少数服从多数”的原则,获得一个更加准确、合理的最终预测结果。具体来说,在分类问题中是用n个弱学习器投票的方式获取最终结果,在回归问题中则是取n个弱学习器的平均值作为最终结果。
Boosting算法
Boosting算法的本质是将弱学习器提升为强学习器,它和Bagging算法的区别在于:Bagging算法对待所有的弱学习器一视同仁;而Boosting算法则会对弱学习器“区别对待”,通俗来讲就是注重“培养精英”和“重视错误”。
”培养精英”就是每一轮训练后对预测结果较准确的弱学习器给予较大的权重,对表现不好的弱学习器则降低其权重。这样在最终预测时,“优秀模型”的权重是大的,相当于它可以投出多票,而“一般模型”只能投出一票或不能投票。
“重视错误”就是在每一轮训练后改变训练集的权值或概率分布,通过提高在前一轮被弱学习器预测错误的样例的权值,降低前一轮被弱学习器预测正确的样例的权值,来提高弱学习器对预测错误的数据的重视程度,从而提升模型的整体预测效果。
1、随机森林
随机森林(Random Forest)是一种经典的Bagging模型,其弱学习器为决策树模型。随机森林模型会在原始数据集中随机抽样,构成n个不同的样本数据集,然后根据这些数据集搭建n个不同的决策树模型,最后根据这些决策树模型的平均值(针对回归模型)或者投票情况(针对分类模型)来获取最终结果。为了保证模型的泛化能力(或者说通用能力),随机森林模型在建立每棵树时,往往会遵循数据随机 和特征随机 这两个基本原则。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import sklearn.ensemble as semodel = se.RandomForestRegressor(max_depth=10 , n_estimators=1000 ,min_samples_split=2 ,random_state=7 ) model.fit(X,y) model=RandomForestClassifier(max_depth=10 , n_estimators=1000 ,min_samples_split=2 ,random_state=7 ) model.fit(X,y)
==案例 ==
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 import numpy as npimport pandas as pdfrom sklearn import datasets as sdfrom sklearn import model_selection as msfrom sklearn import ensemble as sefrom sklearn import metrics as smdata = sd.load_iris() x = data.data y = data.target train_x, test_x, train_y, test_y = ms.train_test_split(x, y, train_size=0.75 , shuffle=True , random_state=7 ) model = se.RandomForestClassifier() model.fit(train_x, train_y) model.feature_importances_ ''' array([0.06178603, 0.0128437 , 0.47481514, 0.45055513]) ''' pred_y = model.predict(test_x) sm.confusion_matrix(test_y, pred_y) sm.f1_score(test_y, pred_y, average='micro' ) ''' 0.8947368421052632 ''' print (sm.classification_report(test_y, pred_y))""" precision recall f1-score support 0 1.00 1.00 1.00 11 1 0.86 0.86 0.86 14 2 0.85 0.85 0.85 13 accuracy 0.89 38 macro avg 0.90 0.90 0.90 38 weighted avg 0.89 0.89 0.89 38 """
2、AdaBoost
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 import numpy as npimport pandas as pdfrom sklearn import datasets as sdfrom sklearn import model_selection as msfrom sklearn import linear_model as lmfrom sklearn import ensemble as sefrom sklearn import metrics as smdata = sd.load_iris() x = data.data y = data.target train_x, test_x, train_y, test_y = ms.train_test_split(x, y, train_size=0.8 , shuffle=True , random_state=21 ) sgd = lm.SGDClassifier() model = se.AdaBoostClassifier(estimator=sgd, algorithm='SAMME' , n_estimators=50 ) model.fit(train_x, train_y) pred_y = model.predict(test_x) print (sm.classification_report(test_y, pred_y))""" precision recall f1-score support 0 1.00 1.00 1.00 11 1 1.00 0.71 0.83 14 2 0.76 1.00 0.87 13 accuracy 0.89 38 macro avg 0.92 0.90 0.90 38 weighted avg 0.92 0.89 0.89 38 """
朴素贝叶斯
贝叶斯分类是机器学习中应用极为广泛的分类算法之一,其产生自英国数学家贝叶斯对于逆概问题的思考。朴素贝叶斯是贝叶斯模型当中最简单的一种。
贝叶斯公式 **: **
$$
P(A|B) = P(B|A)P(A) / P(B)
$$
其中P(A)为事件A发生的概率,P(B)为事件B发生的概率,P(A|B)表示在事件B发生的条件下事件A发生的概率,同理P(B|A)则表示在事件A发生的条件下事件B发生的概率。
朴素贝叶斯分类是贝叶斯分类中最简单,也是常见的一种分类方法 。
$$
P( Y|X1,X2,…,Xn ) = p( X1,X2,…Xn|Y) P(Y) / P(X1,X2,…Xn)
$$
朴素贝叶斯模型假设给定目标值后各个特征之间相互独立,分子的计算公式可以写成如下形式:
$$
P( X1,X2,…Xn|Y)P(Y) = P(X1|Y)P(X2|Y)P(X3|Y)…P(Xn|Y)P(Y)
$$
。其中P(X1|Y)、P(X2|Y)、P(Y)
等数据都是已知的,由此可以计算在n个特征变量取不同的值的条件下,目标变量取某个值的概率,并且选择概率更高者对样本进行分类。
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 import sklearn.naive_bayes as nbimport numpy as npimport pandas as pdfrom sklearn import datasets as sdfrom sklearn import feature_extraction as fefrom sklearn import naive_bayes as nbfrom sklearn import model_selection as msfrom sklearn import metrics as smdata = sd.fetch_20newsgroups() x = data.data y = data.target train_x, test_x, train_y, test_y = ms.train_test_split(x, y, train_size=0.75 , random_state=6 , shuffle=True ) transfer = fe.text.TfidfVectorizer() train_x = transfer.fit_transform(train_x) test_x = transfer.transform(test_x) model = nb.MultinomialNB() model.fit(train_x, train_y) pred_y = model.predict(test_x) sm.f1_score(test_y, pred_y, average='micro' ) """ 0.8462354188759279 """
支持向量机(SVM)
支持向量机 (SVM )是一组用于分类、回归和异常值检测的监督学习方法。
支持向量机的优点是:
支持向量机的缺点包括:
分类(SVC)
SVC,NuSVC、LinearSVC是能够对数据集执行二进制和多类分类的类。
SVC和NuSVC是类似的方法,但接受的参数集略有不同,并具有不同的数学公式。另一方面,LinearSVC对于线性核的情况,是支持向量分类的另一种(更快)实现。请注意, LinearSVC不接受参数kernel,因为它被假定为线性的。
作为其他分类器,SVC、NuSVC和 LinearSVC将两个数组作为输入:一个包含训练样本X的形状数组,以及一个形状类标签(字符串或整数)数组:(n_samples, n_features) y(n_samples)
1 2 3 4 5 sklearn.svm.LinearSVC(penalty='l2' , loss='squared_hinge' , *, dual=True , tol=0.0001 , C=1.0 , multi_class='ovr' , fit_intercept=True , intercept_scaling=1 , class_weight=None , verbose=0 , random_state=None , max_iter=1000 ) sklearn.svm.NuSVC(*, nu=0.5 , kernel='rbf' , degree=3 , gamma='scale' , coef0=0.0 , shrinking=True , probability=False , tol=0.001 , cache_size=200 , class_weight=None , verbose=False , max_iter=-1 , decision_function_shape='ovr' , break_ties=False , random_state=None ) sklearn.svm.SVC(*, C=1.0 , kernel='rbf' , degree=3 , gamma='scale' , coef0=0.0 , shrinking=True , probability=False , tol=0.001 , cache_size=200 , class_weight=None , verbose=False , max_iter=-1 , decision_function_shape='ovr' , break_ties=False , random_state=None )
回归(SVR)
支持向量分类的方法可以扩展到解决回归问题。这种方法称为支持向量回归。
SVM算法非常全面,它不仅支持线性和非线性分类,而且还支持线性和非线性回归。SVM回归要做的就是让尽可能多的实例位于街道上,同时限制间隔违例(也就是不在街道上的实例)。街道的宽度由超参数ε控制,ε值越大,间隔越大;ε值越小,间隔越小。在间隔内添加更多的实例不会影响模型的预测,所以这个模型被称为ε不敏感。
支持向量分类生成的模型(如上所述)仅依赖于训练数据的一个子集,因为构建模型的成本函数不关心超出边界的训练点。类似地,支持向量回归生成的模型仅依赖于训练数据的一个子集,因为成本函数忽略了预测接近其目标的样本。
支持向量回归有 3 种不同的实现: SVR、NuSVR和LinearSVR
。
LinearSVR
提供比SVR
(仅考虑线性内核)更快的实现,同时NuSVR
实现与SVR
略有不同的公式。
1 2 3 4 5 6 7 sklearn.svm.LinearSVR(*, epsilon=0.0 , tol=0.0001 , C=1.0 , loss='epsilon_insensitive' , fit_intercept=True , intercept_scaling=1.0 , dual=True , verbose=0 , random_state=None , max_iter=1000 ) sklearn.svm.NuSVR(*, nu=0.5 , C=1.0 , kernel='rbf' , degree=3 , gamma='scale' , coef0=0.0 , shrinking=True , tol=0.001 , cache_size=200 , verbose=False , max_iter=- 1 ) sklearn.svm.NuSVR(*, nu=0.5 , C=1.0 , kernel='rbf' , degree=3 , gamma='scale' , coef0=0.0 , shrinking=True , tol=0.001 , cache_size=200 , verbose=False , max_iter=- 1 )
案例
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 import numpy as npimport pandas as pdfrom sklearn import datasets as sdfrom sklearn import model_selection as msfrom sklearn import svmfrom sklearn import metrics as smdata = sd.load_iris() x = data.data y = data.target train_x, test_x, train_y, test_y = ms.train_test_split(x, y, train_size=0.75 , shuffle=True , random_state=7 ) model = svm.SVC() model.fit(train_x, train_y) pred_y = model.predict(test_x) print (sm.classification_report(test_y, pred_y))print ("*****************************************" )sm.f1_score(test_y, pred_y, average=None ) """ precision recall f1-score support 0 1.00 1.00 1.00 11 1 0.76 0.93 0.84 14 2 0.90 0.69 0.78 13 accuracy 0.87 38 macro avg 0.89 0.87 0.87 38 weighted avg 0.88 0.87 0.87 38 ***************************************** array([1. , 0.83870968, 0.7826087 ]) """ model2 = svm.SVR() model2.fit(train_x, train_y) sm.r2_score(test_y, pred_y)
ROC曲线与AUC指标
AUC只能用来评价二分类
AUC非常适合评价样本不平衡中的分类器性能
1 from sklearn.metrics import roc_auc_score
模型保存和加载
1 2 3 4 5 6 7 import joblibjoblib.dump(model, "titanic.pkl" ) model2 = joblib.load("titanic.pkl" ) model2.predict(test_x)
无监督学习
K-means聚类
就是设置K值,在特征空间中随机找K个特征作为聚类中心,然后去找和中心点最近的并标为同一类,全部标为同一颜色后重新寻找这些聚类后的K个中心点,然后重复,当最后聚类的点和初始点一样时候就停止
API
1 sklearn.cluster.KMeans(n_clusters=8 ,init='k-means++' )
评估
外部距离最大化,内部距离最小化
1 2 sklearn.metrics.slihouette_score(特征, 预测值)
信用卡诈骗检测
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 import numpy as npimport pandas as pdfrom sklearn import model_selection as msfrom sklearn import metrics as smfrom sklearn import svmdata = pd.read_csv(f"C:\\Users\\beihai\\workpace\\金砖\\模块C\\数据\\creditcard.csv" , encoding='utf-8' ) x = data.iloc[:,1 :30 ] y = data.iloc[:,-1 :] train_x, test_x, train_y, test_y = ms.train_test_split(x, y, train_size=0.8 , shuffle=True , random_state=21 ) """ print(train_x.shape) print(test_x.shape) (227845, 29) (56962, 29) """ model = svm.SVC() model.fit(train_x, train_y) model.feature_names_in_ """ array(['V1', 'V2', 'V3', 'V4', 'V5', 'V6', 'V7', 'V8', 'V9', 'V10', 'V11', 'V12', 'V13', 'V14', 'V15', 'V16', 'V17', 'V18', 'V19', 'V20', 'V21', 'V22', 'V23', 'V24', 'V25', 'V26', 'V27', 'V28', 'Amount'], dtype=object) """ pred_y = model.predict(test_x) print (sm.classification_report(test_y, pred_y))""" precision recall f1-score support 0 1.00 1.00 1.00 56853 1 0.91 0.29 0.44 109 accuracy 1.00 56962 macro avg 0.96 0.65 0.72 56962 weighted avg 1.00 1.00 1.00 56962 """ sm.f1_score(test_y, pred_y) ''' 0.4444444444444445 '''
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 import numpy as npimport pandas as pdimport matplotlib.pyplot as plt%matplotlib inline data = pd.read_csv(f"C:\\Users\\beihai\\workpace\\金砖\\模块C\\数据\\creditcard.csv" , encoding='utf-8' ) count_classes = pd.value_counts(data['Class' ], sort = True ).sort_index() print (count_classes)count_classes.plot(kind='bar' ) plt.title("Fraud class histograam" ) plt.xlabel("Class" ) plt.ylabel("Frequency" ) from sklearn.preprocessing import StandardScalerdata['normAmount' ] = StandardScaler().fit_transform(data['Amount' ].to_numpy().reshape(-1 ,1 )) data = data.drop(['Time' , 'Amount' ], axis = 1 ) data.head() x = data.loc[:, data.columns != 'Class' ] y = data.loc[:, data.columns == 'Class' ] print (x.shape)print (y.shape)''' (284807, 29) (284807, 1) ''' number_recored_fraud = len (data[data.Class == 1 ]) fraud_indicies = np.array(data[data.Class == 1 ].index) print (number_recored_fraud)print (len (fraud_indicies))''' 492 492 ''' normal_indices = data[data.Class == 0 ].index random_normal_indices = np.random.choice(normal_indices, number_recored_fraud, replace=False ) print (random_normal_indices)random_normal_indices = np.array(random_normal_indices) print (random_normal_indices)under_sample_indices = np.concatenate([fraud_indicies, random_normal_indices]) under_sample_data = data.iloc[under_sample_indices,:] x_undersample = under_sample_data.iloc[:,under_sample_data.columns != 'Class' ] y_undersample = under_sample_data.iloc[:,under_sample_data.columns == 'Class' ] print (len (x_undersample),len (y_undersample)) """ 984 984 """ from sklearn import svmmodel = svm.SVC() model model.fit(train_x, train_y) pred_y = model.predict(test_x) pred_y from sklearn import metrics as smsm.f1_score(test_y, pred_y) ''' 0.9236363636363637 ''' print (sm.classification_report(test_y, pred_y))''' precision recall f1-score support 0 0.88 0.99 0.93 149 1 0.99 0.86 0.92 147 accuracy 0.93 296 macro avg 0.94 0.93 0.93 296 weighted avg 0.94 0.93 0.93 296 '''