为您推荐:
精华内容
最热下载
问答
  • 5星
    135.91MB qq_34351067 2021-04-16 14:11:28
  • 5星
    2KB weixin_40929065 2021-01-15 11:57:36
  • 5星
    6KB weixin_43723517 2021-01-28 22:05:30
  • 5星
    125.57MB u013729183 2021-08-14 01:23:17
  • 5星
    3.44MB weixin_44004081 2021-04-18 23:01:48
  • 5星
    2.37MB weixin_38053509 2021-03-12 15:06:40
  • 5星
    14.28MB qq_38282102 2021-07-26 11:37:49
  • 5星
    181.55MB SKCQTGZX 2021-09-01 14:42:47
  • 5星
    8.36MB wangliang0633 2020-11-19 15:12:18
  • 5星
    370.11MB guoruibin123 2021-04-26 21:23:27
  • 293KB weixin_38610052 2021-01-06 10:48:48
  • 4星
    104KB shiguangcn 2011-06-09 00:08:45
  • netstat -nlpt |grep 100 4.Linux常用命令 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS / DMI) hdparm -i /dev...

    Linux面试题:
    1.Linux常用系统安全命令
    sudo // 超级用户
    su // 用于切换当前用户身份到其他身份,变更时需输入所要变更的用户账号与密码。
    chmod // 用来变更文件或目录的权限
    setfacl // 设置文件访问控制列表
    2.Linux常用进程管理命令
    w // 用于显示已经登陆系统的用户列表,并显示用户正在执行的指令。
    top // 可以实时动态地查看系统的整体运行情况,是一个综合了多方信息监测系统性能和运行信息的实用工具。
    ps // 用于报告当前系统的进程状态。
    kill // 用来删除执行中的程序或工作。
    pkill // 可以按照进程名杀死进程。
    pstree // 以树状图的方式展现进程之间的派生关系,显示效果比较直观。
    killall // 使用进程的名称来杀死进程,使用此指令可以杀死一组同名进程。
    3.Linux常用用户管理命令
    id // 可以显示真实有效的用户ID(UID)和组ID(GID)。
    usermod // 用于修改用户的基本信息。
    useradd // 用于 Linux中创建的新的系统用户。
    groupadd // 用于创建一个新的工作组,新工作组的信息将被添加到系统文件中。
    userdel // 用户删除给定的用户,以及与用户相关的文件。若不加选型,则进删除用户账号,而不删除相关文件。
    4.Linux系统关机和重启
    shutdown reboot
    5.Linux系统定时任务
    contab -e //创建定时任务
    6.Linux文件内容查看
    head // 用于显示文件的开头的内容。在默认情况下,head命令显示文件的头10行内容。
    tail // 用于输入文件中的尾部内容。tail命名默认在屏幕上显示指定文件的末尾10行。
    more // 用来浏览文件档案的内容(只能向前浏览)。
    less // 用来浏览文字档案的内容,允许用户向前或向后浏览文件。使用该命令时,用PageUp键向上翻页,用PageDown键向下翻页。要退出less程序,应按Q键。
    7.计划每星期天早8点服务器定时重启,如何实现?
    Crontab-e
    0008 * * 7 /sbin/init 6
    8.linux下如何改IP,主机名,DNS
    使用setup 命令可以修改IP 和DNS ,修改完后重启网络:service network restart

    临时修改即时生效,重启后失效
    hostname主机名: 修改主机名
    ifconfigeth0 IP netmask 掩码 :修改IP

    永久修改主机名: 修改:/etc/sysconfig/network 文件 HOSTNAME=主机名
    永久修改IP地址:修改 /etc/sysconfig/network-scripts/ifcfg-eth0 文件
    修改完后重启网络:service network restart
    9.如何用mysql 命令进行备份和恢复? 以test 库为例,创建一个备份,并再用此备份进行恢复。
    答案:mysqldump -u root -p test > test.bak
    mysql -u root -p < test.bak
    10.linux系统中如何获取pid为100的进程所监听的tcp端口,请给出详细命令?
    netstat -nlpt |grep 100
    4.Linux常用命令
    arch 显示机器的处理器架构(1) 
    uname -m 显示机器的处理器架构(2) 
    uname -r 显示正在使用的内核版本 
    dmidecode -q 显示硬件系统部件 - (SMBIOS / DMI) 
    hdparm -i /dev/hda 罗列一个磁盘的架构特性 
    hdparm -tT /dev/sda 在磁盘上执行测试性读取操作 
    cat /proc/cpuinfo 显示CPU info的信息 
    cat /proc/interrupts 显示中断 
    cat /proc/meminfo 校验内存使用 
    cat /proc/swaps 显示哪些swap被使用 
    cat /proc/version 显示内核的版本 
    cat /proc/net/dev 显示网络适配器及统计 
    cat /proc/mounts 显示已加载的文件系统 
    lspci -tv 罗列 PCI 设备 
    lsusb -tv 显示 USB 设备 
    date 显示系统日期 
    cal 2007 显示2007年的日历表 
    date 041217002007.00 设置日期和时间 - 月日时分年.秒 
    clock -w 将时间修改保存到 BIOS

    关机 (系统的关机、重启以及登出 ) 
    shutdown -h now 关闭系统(1) 
    init 0 关闭系统(2) 
    telinit 0 关闭系统(3) 
    shutdown -h hours:minutes & 按预定时间关闭系统 
    shutdown -c 取消按预定时间关闭系统 
    shutdown -r now 重启(1) 
    reboot 重启(2) 
    logout 注销

    文件和目录 
    cd /home 进入 ‘/ home’ 目录’ 
    cd … 返回上一级目录 
    cd …/… 返回上两级目录 
    cd 进入个人的主目录 
    cd ~user1 进入个人的主目录 
    cd - 返回上次所在的目录 
    pwd 显示工作路径 
    ls 查看目录中的文件 
    ls -F 查看目录中的文件 
    ls -l 显示文件和目录的详细资料 
    ls -a 显示隐藏文件 
    ls [0-9] 显示包含数字的文件名和目录名 
    tree 显示文件和目录由根目录开始的树形结构(1) 
    lstree 显示文件和目录由根目录开始的树形结构(2) 
    mkdir dir1 创建一个叫做 ‘dir1’ 的目录’ 
    mkdir dir1 dir2 同时创建两个目录 
    mkdir -p /tmp/dir1/dir2 创建一个目录树 
    rm -f file1 删除一个叫做 ‘file1’ 的文件’ 
    rmdir dir1 删除一个叫做 ‘dir1’ 的目录’ 
    rm -rf dir1 删除一个叫做 ‘dir1’ 的目录并同时删除其内容 
    rm -rf dir1 dir2 同时删除两个目录及它们的内容 
    mv dir1 new_dir 重命名/移动 一个目录 
    cp file1 file2 复制一个文件 
    cp dir/* . 复制一个目录下的所有文件到当前工作目录 
    cp -a /tmp/dir1 . 复制一个目录到当前工作目录 
    cp -a dir1 dir2 复制一个目录 
    ln -s file1 lnk1 创建一个指向文件或目录的软链接 
    ln file1 lnk1 创建一个指向文件或目录的物理链接 
    touch -t 0712250000 file1 修改一个文件或目录的时间戳 - (YYMMDDhhmm) 
    file file1 outputs the mime type of the file as text 
    iconv -l 列出已知的编码 
    iconv -f fromEncoding -t toEncoding inputFile > outputFile creates a new from the given input file by assuming it is encoded in fromEncoding and converting it to toEncoding. 
    find . -maxdepth 1 -name *.jpg -print -exec convert “{}” -resize 80x60 “thumbs/{}” ; batch resize files in the current directory and send them to a thumbnails directory (requires convert from Imagemagick)

    文件搜索 
    find / -name file1 从 ‘/’ 开始进入根文件系统搜索文件和目录 
    find / -user user1 搜索属于用户 ‘user1’ 的文件和目录 
    find /home/user1 -name *.bin 在目录 ‘/ home/user1’ 中搜索带有’.bin’ 结尾的文件 
    find /usr/bin -type f -atime +100 搜索在过去100天内未被使用过的执行文件 
    find /usr/bin -type f -mtime -10 搜索在10天内被创建或者修改过的文件 
    find / -name *.rpm -exec chmod 755 ‘{}’ ; 搜索以 ‘.rpm’ 结尾的文件并定义其权限 
    find / -xdev -name *.rpm 搜索以 ‘.rpm’ 结尾的文件,忽略光驱、捷盘等可移动设备 
    locate *.ps 寻找以 ‘.ps’ 结尾的文件 - 先运行 ‘updatedb’ 命令 
    whereis halt 显示一个二进制文件、源码或man的位置 
    which halt 显示一个二进制文件或可执行文件的完整路径

    挂载一个文件系统 
    mount /dev/hda2 /mnt/hda2 挂载一个叫做hda2的盘 - 确定目录 ‘/ mnt/hda2’ 已经存在 
    umount /dev/hda2 卸载一个叫做hda2的盘 - 先从挂载点 ‘/ mnt/hda2’ 退出 
    fuser -km /mnt/hda2 当设备繁忙时强制卸载 
    umount -n /mnt/hda2 运行卸载操作而不写入 /etc/mtab 文件- 当文件为只读或当磁盘写满时非常有用 
    mount /dev/fd0 /mnt/floppy 挂载一个软盘 
    mount /dev/cdrom /mnt/cdrom 挂载一个cdrom或dvdrom 
    mount /dev/hdc /mnt/cdrecorder 挂载一个cdrw或dvdrom 
    mount /dev/hdb /mnt/cdrecorder 挂载一个cdrw或dvdrom 
    mount -o loop file.iso /mnt/cdrom 挂载一个文件或ISO镜像文件 
    mount -t vfat /dev/hda5 /mnt/hda5 挂载一个Windows FAT32文件系统 
    mount /dev/sda1 /mnt/usbdisk 挂载一个usb 捷盘或闪存设备 
    mount -t smbfs -o username=user,password=pass //WinClient/share /mnt/share 挂载一个windows网络共享

    磁盘空间 
    df -h 显示已经挂载的分区列表 
    ls -lSr |more 以尺寸大小排列文件和目录 
    du -sh dir1 估算目录 ‘dir1’ 已经使用的磁盘空间’ 
    du -sk * | sort -rn 以容量大小为依据依次显示文件和目录的大小 
    rpm -q -a --qf ‘%10{SIZE}t%{NAME}n’ | sort -k1,1n 以大小为依据依次显示已安装的rpm包所使用的空间 (fedora, redhat类系统) 
    dpkg-query -W -f=’ I n s t a l l e d − S i z e ; 10 t {Installed-Size;10}t InstalledSize;10t{Package}n’ | sort -k1,1n 以大小为依据显示已安装的deb包所使用的空间 (ubuntu, debian类系统)

    用户和群组 
    groupadd group_name 创建一个新用户组 
    groupdel group_name 删除一个用户组 
    groupmod -n new_group_name old_group_name 重命名一个用户组 
    useradd -c "Name Surname " -g admin -d /home/user1 -s /bin/bash user1 创建一个属于 “admin” 用户组的用户 
    useradd user1 创建一个新用户 
    userdel -r user1 删除一个用户 ( ‘-r’ 排除主目录) 
    usermod -c “User FTP” -g system -d /ftp/user1 -s /bin/nologin user1 修改用户属性 
    passwd 修改口令 
    passwd user1 修改一个用户的口令 (只允许root执行) 
    chage -E 2005-12-31 user1 设置用户口令的失效期限 
    pwck 检查 ‘/etc/passwd’ 的文件格式和语法修正以及存在的用户 
    grpck 检查 ‘/etc/passwd’ 的文件格式和语法修正以及存在的群组 
    newgrp group_name 登陆进一个新的群组以改变新创建文件的预设群组

    文件的权限 - 使用 “+” 设置权限,使用 “-” 用于取消 
    ls -lh 显示权限 
    ls /tmp | pr -T5 -W$COLUMNS 将终端划分成5栏显示 
    chmod ugo+rwx directory1 设置目录的所有人(u)、群组(g)以及其他人(o)以读(r )、写(w)和执行(x)的权限 
    chmod go-rwx directory1 删除群组(g)与其他人(o)对目录的读写执行权限 
    chown user1 file1 改变一个文件的所有人属性 
    chown -R user1 directory1 改变一个目录的所有人属性并同时改变改目录下所有文件的属性 
    chgrp group1 file1 改变文件的群组 
    chown user1:group1 file1 改变一个文件的所有人和群组属性 
    find / -perm -u+s 罗列一个系统中所有使用了SUID控制的文件 
    chmod u+s /bin/file1 设置一个二进制文件的 SUID 位 - 运行该文件的用户也被赋予和所有者同样的权限 
    chmod u-s /bin/file1 禁用一个二进制文件的 SUID位 
    chmod g+s /home/public 设置一个目录的SGID 位 - 类似SUID ,不过这是针对目录的 
    chmod g-s /home/public 禁用一个目录的 SGID 位 
    chmod o+t /home/public 设置一个文件的 STIKY 位 - 只允许合法所有人删除文件 
    chmod o-t /home/public 禁用一个目录的 STIKY 位

    文件的特殊属性 - 使用 “+” 设置权限,使用 “-” 用于取消 
    chattr +a file1 只允许以追加方式读写文件 
    chattr +c file1 允许这个文件能被内核自动压缩/解压 
    chattr +d file1 在进行文件系统备份时,dump程序将忽略这个文件 
    chattr +i file1 设置成不可变的文件,不能被删除、修改、重命名或者链接 
    chattr +s file1 允许一个文件被安全地删除 
    chattr +S file1 一旦应用程序对这个文件执行了写操作,使系统立刻把修改的结果写到磁盘 
    chattr +u file1 若文件被删除,系统会允许你在以后恢复这个被删除的文件 
    lsattr 显示特殊的属性

    打包和压缩文件 
    bunzip2 file1.bz2 解压一个叫做 'file1.bz2’的文件 
    bzip2 file1 压缩一个叫做 ‘file1’ 的文件 
    gunzip file1.gz 解压一个叫做 'file1.gz’的文件 
    gzip file1 压缩一个叫做 'file1’的文件 
    gzip -9 file1 最大程度压缩 
    rar a file1.rar test_file 创建一个叫做 ‘file1.rar’ 的包 
    rar a file1.rar file1 file2 dir1 同时压缩 ‘file1’, ‘file2’ 以及目录 ‘dir1’ 
    rar x file1.rar 解压rar包 
    unrar x file1.rar 解压rar包 
    tar -cvf archive.tar file1 创建一个非压缩的 tarball 
    tar -cvf archive.tar file1 file2 dir1 创建一个包含了 ‘file1’, ‘file2’ 以及 'dir1’的档案文件 
    tar -tf archive.tar 显示一个包中的内容 
    tar -xvf archive.tar 释放一个包 
    tar -xvf archive.tar -C /tmp 将压缩包释放到 /tmp目录下 
    tar -cvfj archive.tar.bz2 dir1 创建一个bzip2格式的压缩包 
    tar -xvfj archive.tar.bz2 解压一个bzip2格式的压缩包 
    tar -cvfz archive.tar.gz dir1 创建一个gzip格式的压缩包 
    tar -xvfz archive.tar.gz 解压一个gzip格式的压缩包 
    zip file1.zip file1 创建一个zip格式的压缩包 
    zip -r file1.zip file1 file2 dir1 将几个文件和目录同时压缩成一个zip格式的压缩包 
    unzip file1.zip 解压一个zip格式压缩包

    RPM 包 - (Fedora, Redhat及类似系统) 
    rpm -ivh package.rpm 安装一个rpm包 
    rpm -ivh --nodeeps package.rpm 安装一个rpm包而忽略依赖关系警告 
    rpm -U package.rpm 更新一个rpm包但不改变其配置文件 
    rpm -F package.rpm 更新一个确定已经安装的rpm包 
    rpm -e package_name.rpm 删除一个rpm包 
    rpm -qa 显示系统中所有已经安装的rpm包 
    rpm -qa | grep httpd 显示所有名称中包含 “httpd” 字样的rpm包 
    rpm -qi package_name 获取一个已安装包的特殊信息 
    rpm -qg “System Environment/Daemons” 显示一个组件的rpm包 
    rpm -ql package_name 显示一个已经安装的rpm包提供的文件列表 
    rpm -qc package_name 显示一个已经安装的rpm包提供的配置文件列表 
    rpm -q package_name --whatrequires 显示与一个rpm包存在依赖关系的列表 
    rpm -q package_name --whatprovides 显示一个rpm包所占的体积 
    rpm -q package_name --scripts 显示在安装/删除期间所执行的脚本l 
    rpm -q package_name --changelog 显示一个rpm包的修改历史 
    rpm -qf /etc/httpd/conf/httpd.conf 确认所给的文件由哪个rpm包所提供 
    rpm -qp package.rpm -l 显示由一个尚未安装的rpm包提供的文件列表 
    rpm --import /media/cdrom/RPM-GPG-KEY 导入公钥数字证书 
    rpm --checksig package.rpm 确认一个rpm包的完整性 
    rpm -qa gpg-pubkey 确认已安装的所有rpm包的完整性 
    rpm -V package_name 检查文件尺寸、 许可、类型、所有者、群组、MD5检查以及最后修改时间 
    rpm -Va 检查系统中所有已安装的rpm包- 小心使用 
    rpm -Vp package.rpm 确认一个rpm包还未安装 
    rpm2cpio package.rpm | cpio --extract --make-directories bin 从一个rpm包运行可执行文件 
    rpm -ivh /usr/src/redhat/RPMS/arch/package.rpm 从一个rpm源码安装一个构建好的包 
    rpmbuild --rebuild package_name.src.rpm 从一个rpm源码构建一个 rpm 包

    YUM 软件包升级器 - (Fedora, RedHat及类似系统) 
    yum install package_name 下载并安装一个rpm包 
    yum localinstall package_name.rpm 将安装一个rpm包,使用你自己的软件仓库为你解决所有依赖关系 
    yum update package_name.rpm 更新当前系统中所有安装的rpm包 
    yum update package_name 更新一个rpm包 
    yum remove package_name 删除一个rpm包 
    yum list 列出当前系统中安装的所有包 
    yum search package_name 在rpm仓库中搜寻软件包 
    yum clean packages 清理rpm缓存删除下载的包 
    yum clean headers 删除所有头文件 
    yum clean all 删除所有缓存的包和头文件

    DEB 包 (Debian, Ubuntu 以及类似系统) 
    dpkg -i package.deb 安装/更新一个 deb 包 
    dpkg -r package_name 从系统删除一个 deb 包 
    dpkg -l 显示系统中所有已经安装的 deb 包 
    dpkg -l | grep httpd 显示所有名称中包含 “httpd” 字样的deb包 
    dpkg -s package_name 获得已经安装在系统中一个特殊包的信息 
    dpkg -L package_name 显示系统中已经安装的一个deb包所提供的文件列表 
    dpkg --contents package.deb 显示尚未安装的一个包所提供的文件列表 
    dpkg -S /bin/ping 确认所给的文件由哪个deb包提供

    APT 软件工具 (Debian, Ubuntu 以及类似系统) 
    apt-get install package_name 安装/更新一个 deb 包 
    apt-cdrom install package_name 从光盘安装/更新一个 deb 包 
    apt-get update 升级列表中的软件包 
    apt-get upgrade 升级所有已安装的软件 
    apt-get remove package_name 从系统删除一个deb包 
    apt-get check 确认依赖的软件仓库正确 
    apt-get clean 从下载的软件包中清理缓存 
    apt-cache search searched-package 返回包含所要搜索字符串的软件包名称

    查看文件内容 
    cat file1 从第一个字节开始正向查看文件的内容 
    tac file1 从最后一行开始反向查看一个文件的内容 
    more file1 查看一个长文件的内容 
    less file1 类似于 ‘more’ 命令,但是它允许在文件中和正向操作一样的反向操作 
    head -2 file1 查看一个文件的前两行 
    tail -2 file1 查看一个文件的最后两行 
    tail -f /var/log/messages 实时查看被添加到一个文件中的内容

    什么是MongoDB?
    答:mongodb是基于分布式文件存储的nosql数据库,是一种文档型数据库,适合存储

    海量数据并提高性能存取。2.mongodb三元素:
    答:数据库、集合(表)、文档(行)

    名字空间(namespace)是什么?
    答:MongoDB存储BSON对象在丛集(collection)中。数据库名字和丛集名字以句点连结起来叫做名字空间(namespace)。

    MongoDB的特点是什么?
      答:(1)面向文档(2)高性能(3)高可用(4)易扩展(5)丰富的查询语言

    MySQL与MongoDB本质之间最基本的差别是什么?
    答:mongodb的本质还是一个数据库产品,3.0以上版本其稳定性和健壮性有很大提升。它与mysql的区别在于它不会遵循一些约束,比如:sql标准、ACID属性,表结构等。其主要特性如下:
     面向集合文档的存储:适合存储Bson(json的扩展)形式的数据;
     格式自由,数据格式不固定,生产环境下修改结构都可以不影响程序运行;
     强大的查询语句,面向对象的查询语言,基本覆盖sql语言所有能力;
     完整的索引支持,支持查询计划;
     支持复制和自动故障转移;
     支持二进制数据及大型对象(文件)的高效存储;
     使用分片集群提升系统扩展性;
     使用内存映射存储引擎,把磁盘的IO操作转换成为内存的操作;。

    各个数据库存储引擎区别?
    答:mysql的存储引擎是针对表进行设置的,一个库的不同表可以设置不同的存储引擎,mysql默认支持多种存储引擎,以适用不同领域的数据库应用需要,主要的几个数据库引擎如下:
     MyISAM存储引擎
    5.5之前默认的存储引擎,不支持事务、不支持外键,表级锁,内存和硬盘空间占用率低,其优势是访问速度快,对事务完整性没有要求,以select、insert为主的应用基本上都可以使用这个引擎;
     InnoDB存储引擎
    5.5之后默认的存储引擎,提供了具有提交、回滚和奔溃恢复能力的事务安全,支持外键并提供了行级锁,其劣势在于写的处理效率相对较低,并且会占用更多的磁盘空间以保留数据和索引;
     MEMORY存储引擎
    使用存于内存中的内容来创建表,MEMORY类型的表数据存于内存访问非常的快,默认使用HASH索引,一旦数据库服务重启或关闭,表中的数据就会丢失;
     MERGE存储引擎
    MERGE存储引擎是一组MyISAM表组合,这些MyISAM表结构完全相同。MERGE表本身没有数据,对MERGE表的CRUD操作都是通过内部的MyISAM表进行的;

    mongoDB 主要使用在什么应用场景?
     答:MongoDB 的应用已经渗透到各个领域,比如游戏、物流、电商、内容管理、社交、物联网、视频直播等,以下是几个实际的应用案例:
     游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、更新
     物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
     社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能
     物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析
     视频直播,使用 MongoDB 存储用户信息、礼物信息等

    6、数据库范式?
    范式 内容
    1NF 每一列都是不可分割的基本数据项,同一列无二值;无重复的域;
    2NF 实例依赖于主键部分;
    3NF 属性不依赖于其他非主属性;
    8、什么情况下设置了索引但无法使用?
    ① 以“%”开头的LIKE语句,模糊匹配
    ② OR语句前后没有同时使用索引
    ③ 数据类型出现隐式转化(如varchar不加单引号的话可能会自动转换为int型
    9、数据库中的事务是什么?
    事务(transaction)是作为一个单元的一组有序的数据库操作。如果组中的所有操作都成功,则认为事务成功,即使只有一个操作失败,事务也不成功。如果所有操作完成,事务则提交,其修改将作用于所有其他数据库进程。如果一个操作失败,则事务将回滚,该事务所有操作的影响都将取消。ACID 四大特性,原子性、隔离性、一致性、持久性。

    了解XSS攻击吗?如何防止SQL注入?
    XSS是跨站脚本攻击,首先是利用跨站脚本漏洞以一个特权模式去执行攻击者构造的脚本,然后利用不安全的Activex控件执行恶意的行为。
    使用htmlspecialchars()函数对提交的内容进行过滤,使字符串里面的特殊符号实体化。
    防止SQL注入的方式:
    开启配置文件中的magic_quotes_gpc 和 magic_quotes_runtime设置
    执行sql语句时使用addslashes进行sql语句转换
    Sql语句书写尽量不要省略双引号和单引号。
    过滤掉sql语句中的一些关键词:update、insert、delete、select、 * 。
    提高数据库表和字段的命名技巧,对一些重要的字段根据程序的特点命名,取不易被猜到的。
    Php配置文件中设置register_globals为off,关闭全局变量注册
    控制错误信息,不要在浏览器上输出错误信息,将错误信息写到日志文件中。

    5、Ndinx与apache的区别?
    Nginx
    轻量级,采用 C 进行编写,同样的 web 服务,会占用更少的内存及资源
    抗并发,nginx 以 epoll and kqueue 作为开发模型,处理请求是异步非阻塞的,负载能力比 apache 高很多,而 apache 则是阻塞型的。在高并发下 nginx 能保持低资源低消耗高性能 ,而 apache 在 PHP 处理慢或者前端压力很大的情况下,很容易出现进程数飙升,从而拒绝服务的现象。
    nginx 处理静态文件好,静态处理性能比 apache 高三倍以上
    nginx 的设计高度模块化,编写模块相对简单
    nginx 配置简洁,正则配置让很多事情变得简单,而且改完配置能使用 -t 测试配置有没有问题,apache 配置复杂 ,重启的时候发现配置出错了,会很崩溃
    nginx 作为负载均衡服务器,支持 7 层负载均衡
    nginx 本身就是一个反向代理服务器,而且可以作为非常优秀的邮件代理服务器
    启动特别容易, 并且几乎可以做到 7*24 不间断运行,即使运行数个月也不需要重新启动,还能够不间断服务的情况下进行软件版本的升级
    社区活跃,各种高性能模块出品迅速
    Apache
    apache 的 rewrite 比 nginx 强大,在 rewrite 频繁的情况下,用 apache
    apache 发展到现在,模块超多,基本想到的都可以找到
    apache 更为成熟,少 bug ,nginx 的 bug 相对较多
    apache 超稳定
    apache 对 PHP 支持比较简单,nginx 需要配合其他后端用
    apache 在处理动态请求有优势,nginx 在这方面是鸡肋,一般动态请求要 apache 去做,nginx 适合静态和反向。
    apache 仍然是目前的主流,拥有丰富的特性,成熟的技术和开发社区

    展开全文
    weixin_44379270 2019-07-24 09:40:04
  • TensorFlow入门 参考资料: ...TensorFlow升级到1.0版本的问题 Tensorflow save&restore遇到问题及解决应对 NotFoundError: Key Variable_10 not found in checkpoint TensorFlow的变量管理...

    TensorFlow入门

    参考资料:

    说明:以下代码示例基于Python3.7和TensorFlow1.13.1

    简介

    TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。它灵活的架构让你可以在多种平台上展开计算,例如台式计算机中的一个或多个CPU(或GPU),服务器,移动设备等等。

    什么是数据流图(Data Flow Graph)?

    数据流图用“结点”(nodes)和“线”(edges)的有向图来描述数学计算。“节点”一般用来表示施加的数学操作,但也可以表示数据输入(feed in)的起点/输出(push out)的终点,或者是读取/写入持久变量(persistent variable)的终点。“线”表示“节点”之间的输入/输出关系。这些数据“线”可以输运“size可动态调整”的多维数据数组,即“张量”(tensor)。张量从图中流过的直观图像是这个工具取名为“Tensorflow”的原因。一旦输入端的所有张量准备好,节点将被分配到各种计算设备完成异步并行地执行运算。

    为什么Tensorflow要使用图模型?图模型有什么优势呢?

    首先,图模型的最大好处是节约系统开销,提高资源的利用率,可以更加高效的进行运算。因为我们在图的执行阶段,只需要运行我们需要的op,这样就大大的提高了资源的利用率;其次,这种结构有利于我们提取中间某些节点的结果,方便以后利用中间的节点去进行其它运算;还有就是这种结构对分布式运算更加友好,运算的过程可以分配给多个CPU或是GPU同时进行,提高运算效率;最后,因为图模型把运算分解成了很多个子环节,所以这种结构也让我们的求导变得更加方便。

    在Anaconda中查找tensorflow,勾选安装即可安装TensorFlow成功。

    基本用法

    使用 TensorFlow, 你必须明白 TensorFlow:

    • 使用图 (graph) 来表示计算任务.
    • 在被称之为 会话 (Session) 的上下文 (context) 中执行图.
    • 使用 tensor 表示数据.
    • 通过 变量 (Variable) 维护状态.
    • 使用 feed 和 fetch 可以为任意的操作(arbitrary operation)赋值或者从其中获取数据.

    综述

    TensorFlow 是一个编程系统, 使用图来表示计算任务. 图中的节点被称之为 op (operation 的缩写). 一个 op 获得 0 个或多个 Tensor, 执行计算, 产生 0 个或多个 Tensor. 每个 Tensor 是一个类型化的多维数组. 例如,你可以将一小组图像集表示为一个四维浮点数数组, 这四个维度分别是 [batch, height, width, channels].

    一个 TensorFlow 图_描述_了计算的过程. 为了进行计算, 图必须在 会话 里被启动.
    会话 将图的 op 分发到诸如 CPU 或 GPU 之类的 设备 上, 同时提供执行 op 的方法.
    这些方法执行后, 将产生的 tensor 返回. 在 Python 语言中, 返回的 tensor 是
    numpy ndarray 对象.

    CPU和GPU之间的区别是什么?

    CPU和GPU是嵌入式和电子系统的基本设备,但它们都可以用于不同的目的。CPU是用于根据操作(例如算术,逻辑,控制和输入 - 输出)执行程序给出的指令的微处理器。相反,GPU最初被设计为在计算机游戏中渲染图像。CPU强调低延迟,而在GPU中,重要性是高吞吐量。

    比较的项目CPUGPU
    代表中央处理器图形处理单元
    专注于低延迟高吞吐量
    擅长处理串行指令处理并行指令
    包含更少的强大核心很多较弱的核心
    特征无序和推测执行的控制逻辑。架构可以容忍内存延迟
    速度有效可以高于CPU的
    内存消耗

    CPU的定义

    **CPU(中央处理器)**是一种主要充当每个嵌入式系统的大脑的设备。它由用于临时存储数据和执行计算的ALU(算术逻辑单元)和执行指令排序和分支的CU(控制单元)组成。它还与计算机的其他单元(例如存储器,输入和输出)交互,用于执行来自存储器的指令,这是接口也是CPU的关键部分的原因。I / O接口有时包含在控制单元中。

    它提供地址、数据和控制信号,同时接收在系统总线的帮助下处理的指令、数据、状态信号和中断。系统总线是一组各种总线,例如地址、控制和数据总线。与GPU不同,CPU为快速缓存分配更多硬件单元,而计算则分配的少。

    GPU的定义

    **GPU(图形处理单元)**是专门用于计算图形显示设计的处理器。它通常与CPU结合用于与CPU共享RAM,这对于大多数计算任务都是有益的。它是高端图形密集处理所必需的。独立GPU单元包含自己的RAM,称为VRAM,用于视频RAM。先进的GPU系统与多核CPU协同工作。起初,图形单元是由英特尔和IBM在20世纪80年代引入的。这些卡具有简单的功能,如区域填充,简单图像的更改,形状绘制等。

    现代图形能够执行研究和分析任务,由于其极端的并行处理,通常超过CPU。在GPU中,几个处理单元被剥离在一起,其中不存在高速缓存一致性。

    计算图

    TensorFlow 程序通常被组织成一个构建阶段和一个执行阶段. 在构建阶段, op 的执行步骤被描述成一个图. 在执行阶段, 使用会话执行图中的 op.

    例如, 通常在构建阶段创建一个图来表示和训练神经网络, 然后在执行阶段反复执行图中的训练 op.

    TensorFlow 支持 C, C++, Python 编程语言. 目前, TensorFlow 的 Python库更加易用, 它提供了大量的辅助函数来简化构建图的工作, 这些函数尚未被 C 和 C++ 库支持.

    三种语言的会话库 (session libraries) 是一致的.

    构建图

    构建图的第一步, 是创建源 op (source op). 源 op 不需要任何输入, 例如 常量 (Constant). 源 op 的输出被传递给其它 op 做运算.

    Python 库中, op 构造器的返回值代表被构造出的 op 的输出, 这些返回值可以传递给其它 op 构造器作为输入.

    TensorFlow Python 库有一个_默认图 (default graph)_, op 构造器可以为其增加节点. 这个默认图对许多程序来说已经足够用了.

    import tensorflow as tf
    
    # 创建一个常量 op, 产生一个 1x2 矩阵. 这个 op 被作为一个节点
    # 加到默认图中.
    #
    # 构造器的返回值代表该常量 op 的返回值.
    matrix1 = tf.constant([[3., 3.]])
    
    # 创建另外一个常量 op, 产生一个 2x1 矩阵.
    matrix2 = tf.constant([[2.],[2.]])
    
    # 创建一个矩阵乘法 matmul op , 把 'matrix1' 和 'matrix2' 作为输入.
    # 返回值 'product' 代表矩阵乘法的结果.
    product = tf.matmul(matrix1, matrix2)
    

    默认图现在有三个节点, 两个 constant() op, 和一个matmul() op. 为了真正进行矩阵相乘运算, 并得到矩阵乘法的结果, 你必须在会话里启动这个图.

    在一个会话中启动图

    构造阶段完成后, 才能启动图. 启动图的第一步是创建一个 Session 对象, 如果无任何创建参数, 会话构造器将启动默认图.

    # 启动默认图.
    sess = tf.Session()
    
    # 调用 sess 的 'run()' 方法来执行矩阵乘法 op, 传入 'product' 作为该方法的参数. 
    # 上面提到, 'product' 代表了矩阵乘法 op 的输出, 传入它是向方法表明, 我们希望取回
    # 矩阵乘法 op 的输出.
    #
    # 整个执行过程是自动化的, 会话负责传递 op 所需的全部输入. op 通常是并发执行的.
    # 
    # 函数调用 'run(product)' 触发了图中三个 op (两个常量 op 和一个矩阵乘法 op) 的执行.
    #
    # 返回值 'result' 是一个 numpy `ndarray` 对象.
    result = sess.run(product)
    print(result)
    # ==> [[ 12.]]
    
    # 任务完成, 关闭会话.
    sess.close()
    

    Session 对象在使用完后需要关闭以释放资源. 除了显式调用 close 外, 也可以使用 “with” 代码块来自动完成关闭动作.

    with tf.Session() as sess:
      result = sess.run([product])
      print result
    

    在实现上, TensorFlow 将图形定义转换成分布式执行的操作, 以充分利用可用的计算资源(如 CPU或 GPU). 一般你不需要显式指定使用 CPU 还是 GPU, TensorFlow 能自动检测. 如果检测到 GPU, TensorFlow 会尽可能地利用找到的第一个 GPU 来执行操作.如果你的系统里有多个 GPU, 那么 ID 最小的 GPU 会默认使用。

    指定设备

    如果你想要手动指派设备, 你可以用 with tf.device 创建一个设备环境, 这个环境下的 operation 都统一运行在环境指定的设备上.

    # 新建一个graph.
    with tf.device('/cpu:0'):
      a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
      b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
    c = tf.matmul(a, b)
    # 新建session with log_device_placement并设置为True.
    sess = tf.Session(config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True))
    # 运行这个op.
    print sess.run(c)
    

    如果你指定的设备不存在, 你会收到 InvalidArgumentError 错误提示。为了避免出现你指定的设备不存在这种情况, 你可以在创建的 session 里把参数 allow_soft_placement 设置为 True, 这样 tensorFlow 会自动选择一个存在并且支持的设备来运行 operation.

    Tensor

    TensorFlow 程序使用 tensor 数据结构来代表所有的数据, 计算图中, 操作间传递的数据都是 tensor.

    你可以把 TensorFlow tensor 看作是一个 n 维的数组或列表. 其中零维张量表示的是一个标量,也就是一个数;一维张量表示的是一个向量,也可以看作是一个一维数组;二维张量表示的是一个矩阵;同理,N维张量也就是N维矩阵。

    # 导入tensorflow模块
    import tensorflow as tf
    
    a = tf.constant([[2.0, 3.0]], name = "a")
    b = tf.constant([[1.0], [4.0]], name = "b")
    result = tf.matmul(a, b, name = "mul")
    print(result)
    
    # 输出
    # Tensor("mul_3:0", shape=(1, 1), dtype=float32)
    

    上述程序的输出结果表明:构建图的运算过程输出的结果是一个Tensor,且其主要由三个属性构成:Name、Shape和Type。Name代表的是张量的名字,也是张量的唯一标识符,我们可以在每个op上添加name属性来对节点进行命名,Name的值表示的是该张量来自于第几个输出结果(编号从0开始),上例中的“mul_3:0”说明是第一个结果的输出。Shape代表的是张量的维度,上例中shape的输出结果(1,1)说明该张量result是一个二维数组,且每个维度数组的长度是1。最后一个属性表示的是张量的类型,每个张量都会有唯一的类型,常见的张量类型如下图所示。

    常用的张量类型

    我们需要注意的是要保证参与运算的张量类型相一致,否则会出现类型不匹配的错误。如下面程序所示,当参与运算的张量类型不同时,Tensorflow会报类型不匹配的错误:

    import tensorflow as tf
    m1 = tf.constant([5, 1])
    m2 = tf.constant([2.0, 4.0])
    result = tf.add(m1, m2)
    
    TypeError: Input 'y' of 'Add' Op has type float32 that does not match type int32 of argument 'x'.
    

    正如程序的报错所示:m1是int32的数据类型,而m2是float32的数据类型,两者的数据类型不匹配,所以发生了错误。所以我们在实际编程时,一定注意参与运算的张量数据类型要相同。

    变量

    变量Variables维护图执行过程中的状态信息.

    下面的例子演示了如何使用变量实现一个简单的计数器.

    # 创建一个变量, 初始化为标量 0.
    state = tf.Variable(0, name="counter")
    
    # 创建一个 op, 其作用是使 state 增加 1
    one = tf.constant(1)
    new_value = tf.add(state, one)
    update = tf.assign(state, new_value)
    
    # 启动图后, 变量必须先经过`初始化` (init) op 初始化,
    # 首先必须增加一个`初始化` op 到图中.
    init_op = tf.global_variables_initializer()
    
    # 启动图, 运行 op
    with tf.Session() as sess:
      # 运行 'init' op
      sess.run(init_op)
      # 打印 'state' 的初始值
      print(sess.run(state))
      # 运行 op, 更新 'state', 并打印 'state'
      for _ in range(3):
        sess.run(update)
        print(sess.run(state))
    
    # 输出:
    
    # 0
    # 1
    # 2
    # 3
    

    代码中 assign() 操作是图所描绘的表达式的一部分, 正如 add() 操作一样. 所以在调用 run() 执行表达式之前, 它并不会真正执行赋值操作.

    通常会将一个统计模型中的参数表示为一组变量. 例如, 你可以将一个神经网络的权重作为某个变量存储在一个 tensor 中. 在训练过程中, 通过重复运行训练图, 更新这个 tensor.

    变量的初始化

    当我们完成了变量的创建,接下来,我们要对变量进行初始化。变量在使用前一定要进行初始化,且变量的初始化必须在模型的其它操作运行之前完成。通常,变量的初始化有三种方式,如下所示:

    # 创建两个变量, 初始化为标量 0.
    W = tf.Variable(0, name="W")
    b = tf.Variable(0, name="b")
    
    # 初始化全部变量
    init = tf.global_variables_initializer()
    with tf.Session() as sess:
      sess.run(init)
    
    # 初始化变量的子集
    init_subset = tf.variables_initializer([W, b], name = "init_subset")
    with tf.Session() as sess:
      sess.run(init_subset)
    
    # 初始化单个变量
    init_var = tf.Variable(tf.zeros([2,5]))
    with tf.Session() as sess:
      sess.run(init_var.initializer)
    

    上述程序说明了初始化变量的三种方式:初始化全部变量、初始化变量的子集以及初始化单个变量。首先,global_variables_initializer()方法是不管全局有多少个变量,全部进行初始化,是最简单也是最常用的一种方式;variables_initializer()是初始化变量的子集,相比于全部初始化化的方式更加节约内存;Variable()是初始化单个变量,函数的参数便是要初始化的变量内容。通过上述的三种方式,我们便可以实现变量的初始化,放心的使用变量了。

    但有时一个变量的初始化依赖于其他变量的初始化,为了确保初始化顺序不会错,可以使用initialized_value()来获取初始化变量的值。你应该使用tf.Variable.initialized_value()而不是变量本身来初始化另一个变量,其值取决于此变量的值。

    # Initialize 'v' with a random tensor.
    v = tf.Variable(tf.truncated_normal([10, 40]))
    # Use `initialized_value` to guarantee that `v` has been
    # initialized before its value is used to initialize `w`.
    # The random values are picked only once.
    w = tf.Variable(v.initialized_value() * 2.0)
    

    变量的保存和恢复

    我们经常在训练模型后,希望保存训练的结果,以便下次再使用或是方便日后查看,这时就用到了Tensorflow变量的保存。变量的保存是通过tf.train.Saver()方法创建一个Saver管理器,来保存计算图模型中的所有变量。具体代码如下:

    var1 = tf.Variable([1,3], name="v1")
    var2 = tf.Variable([2,4], name="v2")
    # 初始化全部变量
    init = tf.global_variables_initializer()
    # 调用Saver()存储器方法
    saver = tf.train.Saver()
    # 启动图
    with tf.Session() as sess:
      sess.run(init)
      # 设置存储路径
      save_path = saver.save(sess, "test/save.ckpt")  
    

    我们要注意,我们的存储文件save.ckpt是一个二进制文件,Saver存储器提供了向该二进制文件保存变量和恢复变量的方法。保存变量的方法就是程序中的save()方法,保存的内容是从变量名到tensor值的映射关系。完成该存储操作后,会在对应目录下生成如下图所示的文件:

    保存变量生成的相应文件

    Saver提供了一个内置的计数器自动为checkpoint文件编号。这就支持训练模型在任意步骤多次保存。此外,还可以通过global_step参数自行对保存文件进行编号,例如:global_step=2,则保存变量的文件夹为model.ckpt-2。

    那如何才能恢复变量呢?首先,我们要知道一定要用和保存变量相同的Saver对象来恢复变量。其次,不需要事先对变量进行初始化。具体代码如下所示:

    # 保存后模型恢复出来用于测试报错:NotFoundError: Key Variable_1 not found in checkpoint
    # 原因:如果模型训练完保存后直接加载,相当于变量在前后定义了两次,
    # 第一次创建的变量name="v1",加载时创建的变量虽然name="v1",
    # 但是实际上name会变成"v1_1"(v1_n-1),
    # 我们在保存的checkpoint中搜索的就是v1_n-1,因为搜索不到所以会报错,提示
    # Key v1_1 not found in checkpoint
    # 解决方法:
    # (1)保存模型后,restart kernel后,再加载测试,就不会出错。
    # (2)在加载过程中,定义 name 相同的变量前面加 tf.reset_default_graph() 
    # 清除默认图的堆栈,并设置全局图为默认图;
    
    # 清除默认图的堆栈
    tf.reset_default_graph() 
    
    var1 = tf.Variable([0,0], name="v1")
    var2 = tf.Variable([0,0], name="v2")
    # 调用Saver()存储器方法
    saver = tf.train.Saver()
    # 读取checkpoint文件
    module_file = tf.train.latest_checkpoint("test/")
    print(module_file)
    # 启动图
    with tf.Session() as sess:
      saver.restore(sess, module_file)
      # 打印变量的值
      # evel()方法用于在session中计算并返回变量的值, 不传参数的话,则使用的是默认的session  
      print(var1.eval())
      print(var2.eval())
    
    # 输出
    # test/save.ckpt
    # INFO:tensorflow:Restoring parameters from test/save.ckpt
    # [1 3]
    # [2 4]  
    

    本程序示例中,我们要注意:变量的获取是通过restore()方法,该方法有两个参数,分别是session和获取变量文件的位置。我们还可以通过latest_checkpoint()方法,获取到该目录下最近一次保存的模型。

    变量作用域

    在深度学习中,你可能需要用到大量的变量集,而且这些变量集可能在多处都要用到。例如,训练模型时,训练参数如权重(weights)、偏置(biases)等已经定下来,要拿到验证集去验证,我们自然希望这些参数是同一组。以往写简单的程序,可能使用全局限量就可以了,但在深度学习中,这显然是不行的,一方面不便管理,另外这样一来代码的封装性受到极大影响。因此,TensorFlow提供了一种变量管理方法:变量作用域机制,以此解决上面出现的问题。

    变量作用域机制在TensorFlow中主要由两部分组成:

    • tf.get_variable(<name>, <shape>, <initializer>):
      通过所给的名字创建或是返回一个变量.
    • tf.variable_scope(<scope_name>): 通过 tf.get_variable()为变量名指定命名空间.

    方法 tf.get_variable() 用来获取或创建一个变量,而不是直接调用tf.Variable.它采用的不是像tf.Variable这样直接获取值来初始化的方法.它的特殊之处在于,他还会搜索是否有同名的变量。一个初始化就是一个方法,创建其形状并且为这个形状提供一个张量.这里有一些在TensorFlow中使用的初始化变量:

    • tf.constant_initializer(value) 初始化一切所提供的值,
    • tf.random_uniform_initializer(a, b)从a到b均匀初始化,
    • tf.random_normal_initializer(mean, stddev) 用所给平均值和标准差初始化均匀分布.

    创建变量作用域用法如下:

    with tf.variable_scope("foo"):
        with tf.variable_scope("bar"):
            v = tf.get_variable("v", [1])
            assert v.name == "foo/bar/v:0"
    

    方法tf.variable_scope(scope_name),它会管理在名为scope_name的域(scope)下传递给tf.get_variable的所有变量名(组成了一个变量空间),根据规则确定这些变量是否进行复用。这个方法最重要的参数是reuse,有None,tf.AUTO_REUSE与True三个选项。具体用法如下:

    1. reuse的默认选项是None,此时会继承父scope的reuse标志。

    2. 自动复用(设置reuse为tf.AUTO_REUSE),如果变量存在则复用,不存在则创建。这是最安全的用法,在使用新推出的EagerMode时reuse将被强制为tf.AUTO_REUSE选项。用法如下:

      def foo():
        with tf.variable_scope("foo", reuse=tf.AUTO_REUSE):
          v = tf.get_variable("v", [1])
        return v
      
      v1 = foo()  # Creates v.
      v2 = foo()  # Gets the same, existing v.
      assert v1 == v2
      
    3. 复用(设置reuse=True):

      with tf.variable_scope("foo"):
        v = tf.get_variable("v", [1])
      with tf.variable_scope("foo", reuse=True):
        v1 = tf.get_variable("v", [1])
      assert v1 == v
      
    4. 捕获某一域并设置复用(scope.reuse_variables()):

      with tf.variable_scope("foo") as scope:
        v = tf.get_variable("v", [1])
        scope.reuse_variables()
        v1 = tf.get_variable("v", [1])
      assert v1 == v
      

      1)非复用的scope下再次定义已存在的变量;或2)定义了复用但无法找到已定义的变量,TensorFlow都会抛出错误,具体如下:

      with tf.variable_scope("foo"):
          v = tf.get_variable("v", [1])
          v1 = tf.get_variable("v", [1])
          #  Raises ValueError("... v already exists ...").
      
      with tf.variable_scope("foo", reuse=True):
          v = tf.get_variable("v", [1])
          #  Raises ValueError("... v does not exists ...").
      

    Fetch

    为了取回操作的输出内容, 可以在使用 Session 对象的 run() 调用 执行图时, 传入一些 tensor, 这些 tensor 会帮助你取回结果. 在之前的例子里, 我们只取回了单个节点 state, 但是你也可以取回多个tensor:

    input1 = tf.constant(3.0)
    input2 = tf.constant(2.0)
    input3 = tf.constant(5.0)
    intermed = tf.add(input2, input3)
    mul = tf.multiply(input1, intermed)
    
    with tf.Session() as sess:
      result = sess.run([mul, intermed])
      print(result)
    
    # 输出:
    # [21.0, 7.0]
    

    需要获取的多个 tensor 值,在 op 的一次运行中一起获得(而不是逐个去获取 tensor)。

    Feed

    上述示例在计算图中引入了 tensor, 以常量或变量的形式存储. TensorFlow 还提供了 feed 机制, 该机制可以临时替代图中的任意操作中的 tensor, 可以对图中任何操作提交补丁, 直接插入一个 tensor.

    feed 使用一个 tensor 值临时替换一个操作的输出结果. 你可以提供 feed 数据作为 run() 调用的参数.

    feed 只在调用它的方法内有效, 方法结束, feed 就会消失. 最常见的用例是将某些特殊的操作指定为 “feed” 操作, 标记的方法是使用 tf.placeholder() 为这些操作创建占位符.

    placeholder是一个数据初始化的容器,它与变量最大的不同在于placeholder定义的是一个模板,这样我们就可以session运行阶段,利用feed_dict的字典结构给placeholder填充具体的内容,而无需每次都提前定义好变量的值,大大提高了代码的利用率。

    input1 = tf.placeholder(tf.float32)
    input2 = tf.placeholder(tf.float32)
    output = tf.multiply(input1, input2)
    
    with tf.Session() as sess:
      print(sess.run([output], feed_dict={input1:[7.], input2:[2.]}))
    
    # 输出:
    # [array([ 14.], dtype=float32)]
    

    如果没有正确提供 feed, placeholder() 操作将会产生错误.

    TensorBoard

    1. Tensorboard简介

    对大部分人而言,深度神经网络就像一个黑盒子,其内部的组织、结构、以及其训练过程很难理清楚,这给深度神经网络原理的理解和工程化带来了很大的挑战。为了解决这个问题,tensorboard应运而生。Tensorboard是tensorflow内置的一个可视化工具,它通过将tensorflow程序输出的日志文件的信息可视化使得tensorflow程序的理解、调试和优化更加简单高效。Tensorboard的可视化依赖于tensorflow程序运行输出的日志文件,因而tensorboard和tensorflow程序在不同的进程中运行。

    那如何启动tensorboard呢?下面代码定义了一个简单的用于实现向量加法的计算图。

    import tensorflow as tf  
    # 定义一个计算图,实现两个向量的减法操作  
    # 定义两个输入,a为常量,b为变量  
    a=tf.constant([10.0, 20.0, 40.0], name='a')  
    b=tf.Variable(tf.random_uniform([3]), name='b')  
    output=tf.add_n([a,b], name='add')  
    # 生成一个具有写权限的日志文件操作对象,将当前命名空间的计算图写进日志中  
    writer=tf.summary.FileWriter('/path/to/logs', tf.get_default_graph())  
    writer.close()
    

    在上面程序的8、9行中,创建一个writer,将tensorboard summary写入文件夹/path/to/logs,然后运行上面的程序,在程序定义的日志文件夹/path/to/logs目录下,生成了一个新的日志文件events.out.tfevents.1524711020.bdi-172,如下图1所示。当然,这里的日志文件夹也可以由读者自行指定,但是要确保文件夹存在。如果使用的tensorboard版本比较低,那么直接运行上面的代码可能会报错,此时,可以尝试将第8行代码改为file_writer=tf.train.SummaryWriter(‘/path/to/logs’, sess.graph)

    图1 日志目录下生成的events文件路径

    接着运行如图2所示命令tensorboard --logdir /path/to/logs来启动服务。

    图2 linux下启动tensorboard服务的命令

    注意,当系统报错,找不到tensorboard命令时,则需要使用绝对路径调用tensorboard,例如下面的命令形式:

    python tensorflow/tensorboard/tensorboard.py --logdir=path/to/log-directory

    图3 tensorflow向量相加程序的计算图的可视化结果

    启动tensorboard服务后,在本地浏览器中输入http://188.88.88.88:6006,会看到如上图3所示的界面。注意,由于本节程序是在Linux服务器上运行的,所以需要输入该服务器完整的IP地址(http://188.88.88.88:6006指本实验所使用的服务器IP地址,实际操作时需要修改成实际使用的服务器IP),若tensorflow程序是在本机上运行,则需将上述IP地址http://188.88.88.88:6006替换成localhost。

    根据上述内容描述,tensorboard的启动过程可以概括为以下几步:

    1.创建writer,写日志文件
    writer=tf.summary.FileWriter('/path/to/logs', tf.get_default_graph())
    2.保存日志文件
    writer.close()
    3.运行可视化命令,启动服务
    tensorboard --logdir /path/to/logs

    4.打开可视化界面

    通过浏览器打开服务器访问端口http://xxx.xxx.xxx.xxx:6006

    注意:tensorboard兼容Google浏览器或Firefox浏览器,对其他浏览器的兼容性较差,可能会提示bug或出现其他性能上的问题。

    图4 tensorboard各栏目的默认界面

    在这里使用tensorboard1.13.1,较以往版本有很多不同。首先从界面上,此版本的tensorboard导航栏中只显示有内容的栏目,如GRAPHS,其他没有相关数据的子栏目都隐藏在INACTIVE栏目中,点击这些子栏目则会显示一条如图4所示的提示信息,指示使用者如何序列化相关数据。除此之外,在栏目的数量上也有增加,新增了DISTRIBUTIONS、PROJECTOR、TEXT、PR CURVES、PROFILE五个栏目。

    Tensorboard的可视化功能很丰富。SCALARS栏目展示各标量在训练过程中的变化趋势,如accuracy、cross entropy、learning_rate、网络各层的bias和weights等标量。如果输入数据中存在图片、视频,那么在IMAGES栏目和AUDIO栏目下可以看到对应格式的输入数据。在GRAPHS栏目中可以看到整个模型计算图结构。在HISTOGRAM栏目中可以看到各变量(如:activations、gradients,weights 等变量)随着训练轮数的数值分布,横轴上越靠前就是越新的轮数的结果。DISTRIBUTIONS和HISTOGRAM是两种不同形式的直方图,通过这些直方图可以看到数据整体的状况。PROJECTOR栏目中默认使用PCA分析方法,将高维数据投影到3D空间,从而显示数据之间的关系。

    2. Tensorflow数据流图

    从tensorboard中我们可以获取更多,远远不止图3所展示的。这一小节将从计算图结构和结点信息两方面详细介绍如何理解tensorboard中的计算图,以及从计算图中我们能获取哪些信息。

    2.1 Tensorflow的计算图结构

    如上图3展示的是一个简单的计算图,图结构中主要包含了以下几种元素:

    : Namespace,表示命名空间

    :OpNode,操作结点

    :Constant,常量

    :Dataflow edge,数据流向边,显示两个操作之间的tensor流程

    :Control dependency edge,控制依赖边

    :Reference edge,参考边

    除此之外,还有Unconnected series、Connected series、Summary等元素。

    断线节点序列
    :彼此之间不连接的有限个节点序列。这个结构上的简化法叫做序列折叠(series collapsing)。 序列基序(Sequential motifs)是拥有相同结构并且其名称结尾的数字不同的节点,它们被折叠进一个单独的节点块(stack)中。对长序列网络来说,序列折叠极大地简化了视图,对于已层叠的节点,双击会展开序列。

    相连节点序列
    :彼此之间相连的有限个节点序列

    摘要节点:摘要节点

    引用边:引用边,表示出度操作节点可以使入度tensor发生变化。

    这些元素构成的计算图能够让我们对输入数据的流向,各个操作之间的关系等有一个清晰的认识。

    图5 初始的计算图结构

    如上图5,是一个简单的两层全连接神经网络的计算图。仅仅从图5,我们很难快速了解该神经网络的主体数据流关系,因为太多的细节信息堆积在了一起。这还只是一个两层的简单神经网络,如果是多层的深度神经网络,其标量的声明,常量、变量的初始化都会产生新的计算结点,这么多的结点在一个页面上,那其对应的计算图的复杂性,排列的混乱性难以想象。所以我们需要对计算图进行整理,避免主要的计算节点淹没在大量的信息量较小的节点中,让我们能够更好的快速抓住主要信息。通过定义子命名空间,可以达到整理节点、让可视化效果更加清晰的目的。

    图6 整理后的计算图结构

    如上图6,就是通过定义子命名空间整理结点后的效果。该计算图只显示了最顶层的各命名空间之间的数据流关系,其细节信息被隐藏起来了,这样便于把握主要信息。

    图7为加入子命名空间后的部分代码截图。代码中,将输入数据都放在了input命名空间中,还使用了perdition、moving_averages、loss、train等命名空间去整理对应的操作过程。

    图7 用命名空间整理计算图的代码截图

    图8 手动将节点从主图中移除

    除此之外,我们还可以通过手动将不重要的节点从主图中移除来简化计算图,如上图8,右键点击想要移除的节点,会出现“Remove from main graph”按钮,点击该按钮,就可以移除对应节点了。

    2.2 结点的信息

    Tensorboard除了可以展示整体的计算图结构之外,还可以展示很多细节信息,如结点的基本信息、运行时间、运行时消耗的内存、各结点的运行设备(GPU或者CPU)等。

    2.2.1 基本信息

    前面的部分介绍了如何将计算图的细节信息隐藏起来,但是有的时候,我们需要查看部分重要命名空间下的节点信息,那这些细节信息如何查看呢?对于节点信息,双击图8中的任意一个命名空间,就会展开对应命名空间的细节图(再次双击就可以收起细节图)。

    图9 展开input命名空间节点信息图

    上图9是input命名空间的展开图,展开图中包含了两个操作节点(x_input和y_input)。除了了解具体包含的操作节点以及其他元素外,我们还可以获取粒度更小的信息。

    图10 input命名空间的放大的细节图

    图11 命名空间的节点信息

    图12 计算节点的基本信息

    上图10所示为图9中input命名空间展开图的放大图。观察图10,我们可以了解到输入数据x、y的维度,图中x的向量维度为784维,y为10维,?表示样本数量。本节演示中使用的是mnist数据集,mnist数据集是一个针对图片的10分类任务,输入向量维度是784,这说明可以通过计算图上这些信息,来校验输入数据是否正确。通过左键单击命名空间或者操作节点,屏幕的右上角会显示对应的具体信息。

    如上图11中,右上角绿色框标注的部分为命名空间layer2的具体信息。如上图12中,右上角绿色框标注的部分为节点x_input的具体信息。

    2.2.2 其他信息

    除了节点的基本信息之外,tensorboard还可以展示每个节点运行时消耗的时间、空间、运行的机器(GPU或者CPU)等信息。本小节将详细讲解如何使用tensorboard展示这些信息。这些信息有助于快速获取时间、空间复杂度较大的节点,从而指导后面的程序优化。

    将2.1节中图7所展示的代码的session部分改成如下所示的程序,就可以将程序运行过程中不同迭代轮数中tensorflow各节点消耗的时间和空间等信息写入日志文件中,然后通过读取日志文件将这些信息用tensorboard展示出来。

    #创建writer对象  
    writer=tf.summary.FileWriter("/path/to/metadata_logs",tf.get_default_graph())  
    with tf.Session() as sess:  
        tf.global_variables_initializer().run()  
        for i in range(TRAINING_STEPS):  
            x_batch, y_batch=mnist.train.next_batch(BATCH_SIZE)  
            if i%1000==0:  
                #这里通过trace_level参数配置运行时需要记录的信息,  
                # tf.RunOptions.FULL_TRACE代表所有的信息  
                run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)  
                #运行时记录运行信息的proto,pb是用来序列化数据的  
                run_metadata = tf.RunMetadata()  
                #将配置信息和记录运行信息的proto传入运行的过程,从而记录运行时每一个节点的时间、空间开销信息  
                _, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: x_batch, y_: y_batch}, options=run_options, run_metadata=run_metadata)  
                #将节点在运行时的信息写入日志文件  
                writer.add_run_metadata(run_metadata, 'step %03d' % i)  
            else:  
                _, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: xs, y_: ys})  
        writer.close()
    

    运行上面的程序,生成日志文件存储在/path/to/metadata_logs/目录下,启动tensorboard服务,读取日志文件信息,将每一个节点在不同迭代轮数消耗的时间、空间等信息展示出来。

    图13 选择迭代轮数对应记录页面

    如上图13所示,在浏览器中打开可视化界面,进入GRAPHS子栏目,点击Session runs选框,会出现一个下拉菜单,这个菜单中展示了所有日志文件中记录的运行数据所对应的迭代轮数。任意选择一个迭代轮数,页面右边的区域会显示对应的运行数据。

    图14 第9000轮迭代时不同计算节点消耗时间的可视化效果图

    图15 第9000轮迭代时不同计算节点占有存储的可视化效果图

    如上图14所示,选择了第9000轮的运行数据,然后选择Color栏目下的Compute time选项,GRAPHS栏目下就会显示tensorflow程序每个计算节点的运行时间。图中使用颜色的深浅来表示运行时间的长短,颜色深浅对应的具体运行时间可以从页面左侧的颜色条看出。由图14可知,train命名空间运行时所消耗的时间最长,Variable命名空间所消耗的时间比较短,无色表示不消耗时间。

    如上图15展示了tensorflow各个节点所占用的空间大小。与衡量运行时所消耗的时间方法类似,使用颜色的深浅来标识所占用内存的大小。颜色条上的数字说明,占用的最大空间为677MB,最小空间为0B。train命名空间占用的存储空间最大。

    除了时间和空间指标,tensorboard还可以展示各节点的运行设备(GPU还是CPU)、XLA Cluster、TPU Compatibility等,这些全部都在Color栏目下作为选项供选择。这些指标都是将节点染色,通过不同颜色以及颜色深浅来标识结果的。如下图16,是TPU Compatibility展示图。

    图16 第9000轮迭代时不同计算节点的TPU Compatibility效果展示图

    3. Tensorflow监控指标可视化

    除了GRAPHS栏目外,tensorboard还有IMAGES、AUDIO、SCALARS、HISTOGRAMS、DISTRIBUTIONS、FROJECTOR、TEXT、PR CURVES、PROFILE九个栏目,本小节将详细介绍这些子栏目各自的特点和用法。

    3.1 IMAGES

    图像仪表盘,可以显示通过tf.summary.image()函数来保存的png图片文件。

    # 指定图片的数据源为输入数据x,展示的相对位置为[-1,28,28,1]
    image_shape=tf.reshape(x, [-1, 28, 28,1])  
    # 将input命名空间下的图片放到summary中,一次展示10张  
    tf.summary.image('input', image_shape, 10) 
    

    如上面代码,将输入数据中的png图片放到summary中,准备后面写入日志文件。运行程序,生成日志文件,然后在tensorboard的IMAGES栏目下就会出现如下图17所示的内容(实验用的是mnist数据集)。仪表盘设置为每行对应不同的标签,每列对应一个运行。图像仪表盘仅支持png图片格式,可以使用它将自定义生成的可视化图像(例如matplotlib散点图)嵌入到tensorboard中。该仪表盘始终显示每个标签的最新图像。

    图17 tensorboard中的IMAGES栏目内容展开界面

    3.2 AUDIO

    音频仪表盘,可嵌入音频的小部件,用于播放通过tf.summary.audio()函数保存的音频。

    tf.summary.audio('audio', audio, sampling_frequency)
    

    audio是一个三维或者二维tensor,含义是[音频数, 每个音频的帧数, 每个音频的通道数]或者[音频数, 每个音频的帧数]。

    sampling_frequency是音频的采样率。

    仪表盘设置为每行对应不同的标签,每列对应一个运行。该仪表盘始终嵌入每个标签的最新音频。

    3.3 SCALARS

    Tensorboard 的标量仪表盘,统计tensorflow中的标量(如:学习率、模型的总损失)随着迭代轮数的变化情况。如下图18所示,SCALARS栏目显示通过函数tf.summary.scalar()记录的数据的变化趋势。如下所示代码可添加到程序中,用于记录学习率的变化情况。

    # 在learning_rate附近添加,用于记录learning_rate  
    tf.summary.scalar('learning_rate', learning_rate)  
    

    Scalars栏目能进行的交互操作有:

    • 点击每个图表左下角的蓝色小图标将展开图表
    • 拖动图表上的矩形区域将放大
    • 双击图表将缩小
    • 鼠标悬停在图表上会产生十字线,数据值记录在左侧的运行选择器中。

    图18 tensorboard中的SCALARS栏目内容展开界面

    此外,读者可通过在仪表盘左侧的输入框中,编写正则表达式来创建新文件夹,从而组织标签。

    3.4 HISTOGRAMS

    Tensorboard的张量仪表盘,统计tensorflow中的张量随着迭代轮数的变化情况。它用于展示通过tf.summary.histogram记录的数据的变化趋势。如下代码所示:

    tf.summary.histogram(weights, 'weights')

    上述代码将神经网络中某一层的权重weight加入到日志文件中,运行程序生成日志后,启动tensorboard就可以在HISTOGRAMS栏目下看到对应的展开图像,如下图19所示。每个图表显示数据的时间“切片”,其中每个切片是给定步骤处张量的直方图。它依据的是最古老的时间步原理,当前最近的时间步在最前面。通过将直方图模式从“偏移”更改为“叠加”,如果是透视图就将其旋转,以便每个直方图切片都呈现为一条相互重叠的线。

    图19 tensorboard中的HISTOGRAMS栏目内容展开界面

    3.5 DISTRIBUTIONS

    Tensorboard的张量仪表盘,相较于HISTOGRAMS,用另一种直方图展示从tf.summary.histogram()函数记录的数据的规律。它显示了一些分发的高级统计信息。

    如下图20所示,图表上的每条线表示数据分布的百分位数,例如,底线显示最小值随时间的变化趋势,中间的线显示中值变化的方式。从上至下看时,各行具有以下含义:[最大值,93%,84%,69%,50%,31%,16%,7%,最小值]。这些百分位数也可以看作标准偏差的正态分布:[最大值,μ+1.5σ,μ+σ,μ+0.5σ,μ,μ-0.5σ,μ-σ,μ-1.5σ,最小值],使得从内侧读到外侧的着色区域分别具有宽度[σ,2σ,3σ]。

    图20 tensorboard中的DISTRIBUTIONS栏目内容展开界面

    3.6 PROJECTOR

    嵌入式投影仪表盘,全称Embedding Projector,是一个交互式的可视化工具,通过数据可视化来分析高维数据。例如,读者可在模型运行过程中,将高维向量输入,通过embedding projector投影到3D空间,即可查看该高维向量的形式,并执行相关的校验操作。Embedding projector的建立主要分为以下几个步骤:

    1)建立embedding tensor

    #1. 建立 embeddings  
    embedding_var = tf.Variable(batch_xs, name="mnist_embedding")  
    summary_writer = tf.summary.FileWriter(LOG_DIR) 
    

    2)建立embedding projector 并配置

    config = projector.ProjectorConfig()  
    embedding = config.embeddings.add()  
    embedding.tensor_name = embedding_var.name  
    embedding.metadata_path = path_for_mnist_metadata   #'metadata.tsv'  
    embedding.sprite.image_path = path_for_mnist_sprites  #'mnistdigits.png'  
    embedding.sprite.single_image_dim.extend([28,28])  
    projector.visualize_embeddings(summary_writer, config)
    

    3)将高维变量保存到日志目录下的checkpoint文件中

    sess = tf.InteractiveSession()  
    sess.run(tf.global_variables_initializer())  
    saver = tf.train.Saver()  
    saver.save(sess, os.path.join(LOG_DIR, "model.ckpt"), 1) 
    

    4)将metadata与embedding联系起来,将 vector 转换为 images,反转灰度,创建并保存 sprite image

    to_visualise = batch_xs  
    to_visualise = vector_to_matrix_mnist(to_visualise)  
    to_visualise = invert_grayscale(to_visualise)  
    sprite_image = create_sprite_image(to_visualise)  
    plt.imsave(path_for_mnist_sprites,sprite_image,cmap='gray') 
    

    5)运行程序,生成日志文件,启动服务,tensorboard中的PROJECTOR栏将展示投影后的数据的动态图,如下图21所示。

    图21 tensorboard中的PROJECTOR栏目内容展开界面

    Embedding Projector从模型运行过程中保存的checkpoint文件中读取数据,默认使用主成分分析法(PCA)将高维数据投影到3D空间中,也可以设置选择另外一种投影方法,T-SNE。除此之外,也可以使用其他元数据进行配置,如词汇文件或sprite图片。

    3.7 TEXT

    文本仪表盘,显示通过tf.summary.text()函数保存的文本片段,包括超链接、列表和表格在内的Markdown功能均支持。

    3.8 PR CURVES

    PR CURVES仪表盘显示的是随时间变化的PR曲线,其中precision为横坐标,recall为纵坐标。如下代码创建了一个用于记录PR曲线的summary。

    # labels为输入的y, predition为预测的y值  
    # num_thresholds为多分类的类别数量  
    tensorboard.summary.pr_curve(name='foo',  
                         predictions=predictions,  
                         labels=labels,  
                         num_thresholds=11)  
    

    图22 tensorboard中的PR CURVES栏目内容展开界面

    上图22为tensorboard上PR CURVES栏目在有内容时的首页,没有内容时就隐藏在INACTIVE栏目下。

    训练模型时,经常需要在查准率和查全率之间权衡,PR曲线能够帮助我们找到这个权衡点。每条曲线都对应一个二分类问题,所以,针对多分类问题,每一个类都会生成一条对应的PR曲线。

    3.9 PROFILE

    Tensorboard的配置文件仪表盘,该仪表盘上包含了一套TPU工具,可以帮助我们了解,调试,优化tensorflow程序,使其在TPU上更好的运行。

    但并不是所有人都可以使用该仪表盘,只有在Google Cloud TPU上有访问权限的人才能使用配置文件仪表盘上的工具。而且,该仪表盘与其他仪表盘一样,都需要在模型运行时捕获相关变量的跟踪信息,存入日志,方可用于展示。

    在PROFILE仪表盘的首页上,显示的是程序在TPU上运行的工作负载性能,它主要分为五个部分:Performance Summary、Step-time Graph、Top 10 Tensorflow operations executed on TPU、Run Environment和Recommendation for Next Step。如下图23所示:

    图23 tensorboard中的PROFILE栏目内容展开界面

    其中,Performance Summary包括以下四项:

    1)所有采样步骤的平均步长时间

    2)主机空闲时间百分比

    3)TPU空闲时间百分比

    4)TPU矩阵单元的利用率

    Run Environment(运行环境)包括以下五方面:

    1)使用的主机数量

    2)使用的TPU类型

    3)TPU内核的数量

    4)训练批次的大小(batch size)

    5)作业信息(构建命令和运行命令)

    4. 总结

    本节主要介绍了tensorflow中一个非常重要的工具——tensorboard。Tensorboard是一个可视化工具,它能够以直方图、折线图等形式展示程序运行过程中各标量、张量随迭代轮数的变化趋势,它也可以显示高维度的向量、文本、图片和音频等形式的输入数据,用于对输入数据的校验。Tensorflow函数与tensorboard栏目的对应关系如下表所示。

    Tensorboard栏目tensorflow日志生成函数内容
    GRAPHS默认保存显示tensorflow计算图
    SCALARStf.summary.scalar显示tensorflow中的张量随迭代轮数的变化趋势
    DISTRIBUTIONStf.summary.histogram显示tensorflow中张量的直方图
    HISTOGRAMStf.summary.histogram显示tensorflow中张量的直方图(以另一种方式)
    IMAGEStf.summary.image显示tensorflow中使用的图片
    AUDIOtf.summary.audio显示tensorflow中使用的音频
    TEXTtf.summary.text显示tensor flow中使用的文本
    PROJECTOR通过读取checkpoint文件可视化高维数据

    Tensorboard的可视化功能对于tensorflow程序的训练非常重要,使用tensorboard进行调参主要分为以下几步:

    1)校验输入数据

    如果输入数据的格式是图片、音频、文本的话,可以校验一下格式是否正确。如果是处理好的低维向量的话,就不需要通过tensorboard校验。

    2)查看graph结构

    查看各个节点之间的数据流关系是否正确,再查看各个节点所消耗的时间和空间,分析程序优化的瓶颈。

    3)查看各变量的变化趋势

    在SCALAR、HISTOGRAMS、DISTRIBUTIONS等栏目下查看accuracy、weights、biases等变量的变化趋势,分析模型的性能

    4)修改code

    根据3)和4)的分析结果,优化代码。

    5)选择最优模型

    6)用Embedding Projector进一步查看error出处

    Tensorboard虽然只是tensorflow的一个附加工具,但熟练掌握tensorboard的使用,对每一个需要对tensorflow程序调优的人都非常重要,它可以显著提高调参工作的效率,帮助我们更快速地找到最优模型。

    读取数据

    1. tensorflow 的数据读取机制

    以图像数据为例,数据读取过程如下所示:

    假设我们的硬盘中有一个图片数据集0001.jpg,0002.jpg,0003.jpg……我们只需要把它们读取到内存中,然后提供给GPU或是CPU进行计算就可以了。这听起来很容易,但事实远没有那么简单。事实上,我们必须要把数据先读入后才能进行计算,假设读入用时0.1s,计算用时0.9s,那么就意味着每过1s,GPU都会有0.1s无事可做,这就大大降低了运算的效率。

    如何解决这个问题?方法就是将读入数据和计算分别放在两个线程中,将数据读入内存的一个队列,如下图所示:

    读取线程源源不断地将文件系统中的图片读入到内存队列中,而负责计算的是另一个线程,计算需要数据时,直接从内存队列中取就可以了。这样就可以解决GPU因为IO而空闲的问题!

    而在tensorflow中,为了方便管理,在内存队列前又添加了一层所谓的**“文件名队列”**。

    为什么要添加这一层文件名队列?首先得了解机器学习中的一个概念:epoch(迭代)。对于一个数据集来讲,运行一个epoch就是将这个数据集中的图片全部计算一遍。如一个数据集中有三张图片A.jpg、B.jpg、C.jpg,那么跑一个epoch就是指对A、B、C三张图片都计算了一遍。两个epoch就是指先对A、B、C各计算一遍,然后再全部计算一遍,也就是说每张图片都计算了两遍。

    tensorflow使用文件名队列+内存队列双队列的形式读入文件,可以很好地管理epoch。下面用图片的形式来说明这个机制的运行方式。还是以数据集A.jpg, B.jpg, C.jpg为例,假定我们要跑一个epoch,那么就在文件名队列中把A、B、C各放入一次,并在之后标注队列结束,如下图。

    程序运行后,内存队列首先读入A(此时A从文件名队列中出队),然后再读取B和C。

    此时,如果再尝试读入,系统由于检测到了“结束”,就会自动抛出一个异常(OutOfRange)。外部捕捉到这个异常后就可以结束程序了。这就是tensorflow中读取数据的基本机制。如果我们要跑2个epoch而不是1个epoch,那只要在文件名队列中将A、B、C依次放入两次再标记结束就可以了。

    2. TensorFlow数据读取机制对应的函数

    如何在TensorFlow中创建这两个内存?

    • 创建文件名队列 - tf.train.string_input_producer 阻塞态 + tf.train.start_queue_runners 激活态

    把输入的数据按照要求排序成一个队列。最常见的是把一堆文件名整理成一个队列。如下操作:

    filenames = [os.path.join(data_dir,'data_batch%d.bin' % i ) for i in xrange(1,6)]
    filename_queue = tf.train.string_input_producer(filenames)
    

    tf.train.string_input_producer有两个重要的参数,一个是num_epochs,它就是上文中提到的epoch数。另一个是shuffle,shuffle是指在epoch内文件顺序是否被打乱。若设置shuffle=False,如下图,每个epoch内,数据还是按照A、B、C的顺序进入文件名队列,这个顺序不会改变。如果设置shuffle=True,那么在epoch内,数据的前后顺序就会被打乱,具体如下图所示。

    **其实,仅仅应用tf.train.string_input_producer构建的文件名队列是处于阻塞态的,并没有真正的将文件名读入到相应的文件名队列内存中,如下左图所示。为了完成在文件名队列内存中构建文件名队列(也就是我们说的读入数据),我们还需要tf.train.start_queue_runners进行启动,**如下右图所示

    我们通常也把tf.train.start_queue_runners叫做‘入栈线程启动器’,使用tf.train.start_queue_runners之后,才会真正启动填充队列的线程,这时系统就不再“阻塞”。此后计算单元就可以拿到数据并进行计算,整个程序也就跑起来了。

    • 创建数据内存序列

    在tensorflow中,数据内存队列不需要自己建立,我们只需要使用reader对象从文件名队列中读取数据就可以了。所以TensorFlow高效读取数据机制中,最重要的是完成文件名队列的设计。

    3. 为什么要使用TFRecords来进行文件的读写?

    在tensorflow中数据的传入方式主要包含以下几种:

    • 供给数据(feed): 在tensorflow程序运行的每一步, 让Python代码来供给数据。
    • 从文件读取数据: 在tensorflow graph的起始, 让一个输入pipeline从文件中读取数据。
    • 预加载数据: 在tensorflow graph中定义常量或变量来保存所有数据(仅适用于数据量比较小的情况)。

    当我们遇到数据集比较大的情况时,第一种和最后一种方法会极其占内存,效率很差。那么为什么使用TFRecords会比较快?在于其使用二进制存储文件,也就是将数据存储在一个内存块中,相比其它文件格式要快很多,特别是如果你使用hdd(Hard Disk Drive)而不是ssd(Solid State Disk),因为它涉及移动磁盘阅读器头并且需要相当长的时间。总体而言,通过使用二进制文件,可以更轻松地分发数据,使数据更好地对齐,以实现高效的读取。

    整个过程分两部分,一是使用tf.train.Example协议流将文件保存成TFRecords格式的.tfrecords文件,这里主要涉及到使用tf.python_io.TFRecordWriter(“train.tfrecords”)tf.train.Example以及tf.train.Features三个函数,第一个是生成需要对应格式的文件,后面两个函数主要是将我们要传入的数据按照一定的格式进行规范化。

    另一部分就是在训练模型时将我们生成的.tfrecords文件读入并传到模型中进行使用。这部分主要涉及到使用tf.TFRecordReader(“train.tfrecords”)tf.parse_single_example两个函数。第一个函数是将我们的二进制文件读入,第二个则是进行解析然后得到我们想要的数据。

    #### 生成train.tfrecords文件 ####
    import os
    import tensorflow as tf 
    from PIL import Image
     
    cwd = os.getcwd()
     
    ''' 数据目录
    -- img1.jpg
         img2.jpg
         img3.jpg
         ...
    -- img1.jpg
         img2.jpg
         ...
    -- ...
    '''
    writer = tf.python_io.TFRecordWriter("train.tfrecords") # 定义train.tfrecords文件
    for index, name in enumerate(classes): # 遍历每一个文件夹
        class_path = cwd + name + "/"      # 每一个文件夹的路径
        for img_name in os.listdir(class_path):  # 遍历每个文件夹中所有的图像
            img_path = class_path + img_name  # 每一张图像的路径
            img = Image.open(img_path)    # 打开图像
            img = img.resize((224, 224))  # 图像裁剪
            img_raw = img.tobytes()       # 将图像转化为bytes
     
            # 调用Example 和 Feature函数将数据格式化保存起来
            # 注意:Features 传入参数为一个字典,方便后续读取数据时的操作
            example = tf.train.Example(features=tf.train.Features(feature={
                "label": tf.train.Feature(int64_list=tf.train.Int64List(value=[index])),
                'img_raw': tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw]))
            }))
            #序列化为字符串,并写入数据
            writer.write(example.SerializeToString())  
    writer.close()
    

    基本的,一个Example中包含Features,Features里包含Feature(这里没s)的字典。最后,Feature里包含有一个 FloatList,或者ByteList,或者Int64List

    就这样,我们把相关的信息都存到了一个文件中,不用单独的label文件,读取也很方便。

    # 从tfrecords文件中读取记录的迭代器
    for serialized_example in tf.python_io.tf_record_iterator("train.tfrecords"):
        example = tf.train.Example()
        example.ParseFromString(serialized_example)
     
        image = example.features.feature['image'].bytes_list.value
        label = example.features.feature['label'].int64_list.value
        # 可以做一些预处理之类的
        print(image, label)
    

    4. 使用队列读取tfrecords数据

    从TFRecords文件中读取数据, 首先需要用tf.train.string_input_producer生成一个解析队列。之后调用tf.TFRecordReader的tf.parse_single_example解析器。其原理如下图:

    解析器首先读取解析队列,返回serialized_example对象,之后调用tf.parse_single_example操作将Example协议缓冲区(protocol buffer)解析为张量。

    def read_and_decode(filename):
        # 根据文件名生成文件名队列
        filename_queue = tf.train.string_input_producer([filename])
        # 定义reader
        reader = tf.TFRecordReader()
        # 返回文件名和文件
        _, serialized_example = reader.read(filename_queue) 
        # 将协议缓冲区Protocol Buffer解析为张量tensor
        # 注意到:我们写文件就是采用了字典的方式进行存储的,所以解析的时候依然用字典进行数据提取
        features = tf.parse_single_example(serialized_example,
                                           features={
                                               'label': tf.FixedLenFeature([], tf.int64),
                                               'img_raw' : tf.FixedLenFeature([], tf.string),
                                           })
        # 将编码为字符串的变量重新变回来,因为写进tfrecord里用to_bytes的形式,也就是字符串
        img = tf.decode_raw(features['img_raw'], tf.uint8)
        # 检查张量形状是否对齐
        img = tf.reshape(img, [224, 224, 3])
        # 图像数据格式化为tf.float32
        img = tf.cast(img, tf.float32) * (1. / 255) - 0.5
        # 标签数据格式化为tf.int32
        label = tf.cast(features['label'], tf.int32)
     
        return img, label
    

    之后,在训练模型过程中,我们就会很方便用这些数据了,例如:

    
    # 解析tfrecords文件的数据
    img, label = read_and_decode("train.tfrecords")
     
    # 通过随机打乱张量的顺序创建batch
    # capacity = ( min_after_dequeue + (num_threads + aSmallSafetyMargin * batch_size) )
    img_batch, label_batch = tf.train.shuffle_batch(
                               [img, label],  # 入队的张量列表
                               batch_size=30, # 进行一次批处理的tensor数
                               capacity=2000, # 队列中最大的元素数
                               min_after_dequeue=1000,# 一次出列操作完成后,队列中元素的最小数量
                               num_threads=4  #使用多个线程在tensor_list中读取文件
                               )
    init = tf.global_variables_initializer()
     
    with tf.Session() as sess:
        sess.run(init)
        # 队列-入栈线程启动器
        threads = tf.train.start_queue_runners(sess=sess)
        for i in range(3):
            val, loss= sess.run([img_batch, label_batch])
    

    三个要点作为总结:

    • tensorflow里的graph能够记住状态,这使得TFRecordReader能够记住tfrecord的位置,并且始终能返回下一个。而这就要求我们在使用之前,必须初始化整个graph,这里使用了函数tf.global_variables_initializer()来进行初始化
    • tensorflow中的队列和普通的队列差不多,不过它里面的operation和tensor都是符号型的,在调用sess.run()时才执行
    • TFRecordReader会一直弹出队列中文件的名字,直到队列为空

    线程和队列

    在使用TensorFlow进行异步计算时,队列是一种强大的机制。

    正如TensorFlow中的其他组件一样,队列就是TensorFlow图中的节点。这是一种有状态的节点,就像变量一样:其他节点可以修改它的内容。具体来说,其他节点可以把新元素插入到队列后端(rear),也可以把队列前端(front)的元素删除。

    为了感受一下队列,让我们来看一个简单的例子。我们先创建一个“先入先出”的队列(FIFOQueue),并将其内部所有元素初始化为零。然后,我们构建一个TensorFlow图,它从队列前端取走一个元素,加上1之后,放回队列的后端。慢慢地,队列的元素的值就会增加。

    EnqueueEnqueueManyDequeue都是特殊的节点。他们需要获取队列指针,而非普通的值,如此才能修改队列内容。我们建议您将它们看作队列的方法。事实上,在Python API中,它们就是队列对象的方法(例如q.enqueue(...))。

    队列使用概述

    队列,如FIFOQueueRandomShuffleQueue,在TensorFlow的张量异步计算时都非常重要。

    例如,一个典型的输入结构:是使用一个RandomShuffleQueue来作为模型训练的输入:

    • 多个线程准备训练样本,并且把这些样本推入队列。
    • 一个训练线程执行一个训练操作,此操作会从队列中移除最小批次的样本(mini-batches)。

    TensorFlow的Session对象是可以支持多线程的,因此多个线程可以很方便地使用同一个会话(Session)并且并行地执行操作。然而,在Python程序实现这样的并行运算却并不容易。所有线程都必须能被同步终止,异常必须能被正确捕获并报告,回话终止的时候, 队列必须能被正确地关闭。

    所幸TensorFlow提供了两个类来帮助多线程的实现:tf.Coordinator
    tf.QueueRunner。从设计上这两个类必须被一起使用。Coordinator类可以用来同时停止多个工作线程并且向那个在等待所有工作线程终止的程序报告异常。QueueRunner类用来协调多个工作线程同时将多个张量推入同一个队列中。

    创建队列

    操作队列的函数主要有:

    • FIFOQueue():创建一个先入先出(FIFO)的队列

    • RandomShuffleQueue():创建一个随机出队的队列

    • enqueue_many():初始化队列中的元素

    • dequeue():出队

    • enqueue():入队

    1、FIFOQueue

    FIFOQueue是先进先出队列,主要是针对一些序列样本。如:在使用循环神经网络的时候,需要处理语音、文字、视频等序列信息的时候,我们希望处理的时候能够按照顺序进行,这时候就需要使用FIFOQueue队列。

    #先入先出队列,初始化队列,设置队列大小5
    q = tf.FIFOQueue(5,"float")
    #入队操作
    init = q.enqueue_many(([1,2,3,4,5],))
    #定义出队操作
    x = q.dequeue()
    y = x + 1
    #将出队的元素加1,然后再加入到队列中
    q_in = q.enqueue([y])
    #创建会话
    with tf.Session() as sess:
        sess.run(init)
        #执行3次q_in操作
        for i in range(3):
            sess.run(q_in)
        #获取队列的长度
        que_len = sess.run(q.size())
        #将队列中的所有元素执行出队操作
        for i in range(que_len):
            print(sess.run(q.dequeue()))
    

    2、RandomShuffleQueue

    RandomShuffleQueue是随机队列,队列在执行出队操作的时候,是以随机的顺序进行的。随机队列一般应用在我们训练模型的时候,希望可以无序的获取样本来进行训练,如:在训练图像分类模型的时候,需要输入的样本是无序的,就可以利用多线程来读取样本,将样本放到随机队列中,然后再利用主线程每次从随机队列中获取一个batch进行模型的训练。

    #初始化一个随机队列,设置队列大小为10,最小长度为2
    q = tf.RandomShuffleQueue(capacity=10,min_after_dequeue=2,dtypes="float")
    #创建会话
    with tf.Session() as sess:
        #定义10次入队操作
        for i in range(10):
            sess.run(q.enqueue(i))
        #定义8次出队操作
        for i in range(8):
            print(sess.run(q.dequeue()))
    

    注意:在使用随机队列的时候,我们设置了队列的容量为10,最小长度为2。当队列的长度已经等于队列的容量(10)再执行入队操作, 或队列的长度已经等于最小长度(2)再执行出队操作时,程序会发生阻断,即程序在执行,但是没有任何输出,如下图:

    定义了10次出队操作,当队列出队8次之后,就被阻断了。我们可以通过设置会话在运行时的等待时间来解除阻断:

    #初始化一个随机队列,设置队列大小为10,最小长度为2
    q = tf.RandomShuffleQueue(capacity=10,min_after_dequeue=2,dtypes="float")
    #创建会话
    with tf.Session() as sess:
        #定义10次入队操作
        for i in range(10):
            sess.run(q.enqueue(i))
        #设置会话运行时等待时间,等待时长为5s
        run_options = tf.RunOptions(timeout_in_ms=5000)
        #定义10次出队操作
        for i in range(10):
            try:
                #当队列进入阻断之后,超时就抛出异常
                print(sess.run(q.dequeue(),options=run_options))
            except tf.errors.DeadlineExceededError:
                print("out of range")
                #退出循环
                break
    

    当队列出队第9次的时候,进入阻断状态时,我们可以通过DeadlineExceededError来捕获阻断信息。

    Coordinator

    Coordinator类用来帮助多个线程协同工作,多个线程同步终止。
    其主要方法有:

    • should_stop():如果线程应该停止则返回True。
    • request_stop(<exception>): 请求该线程停止。
    • join(<list of threads>):等待被指定的线程终止。

    首先创建一个Coordinator对象,然后建立一些使用Coordinator对象的线程。这些线程通常一直循环运行,一直到should_stop()返回True时停止。
    任何线程都可以决定计算什么时候应该停止。它只需要调用request_stop(),同时其他线程的should_stop()将会返回True,然后都停下来。

    # 线程体:循环执行,直到`Coordinator`收到了停止请求。
    # 如果某些条件为真,请求`Coordinator`去停止其他线程。
    def MyLoop(coord):
      while not coord.should_stop():
        ...do something...
        if ...some condition...:
          coord.request_stop()
    
    # Main code: create a coordinator.
    coord = Coordinator()
    
    # Create 10 threads that run 'MyLoop()'
    threads = [threading.Thread(target=MyLoop, args=(coord)) for i in xrange(10)]
    
    # Start the threads and wait for all of them to stop.
    for t in threads: t.start()
    coord.join(threads)
    

    QueueRunner

    QueueRunner类会创建一组线程, 这些线程可以重复的执行Enquene操作, 他们使用同一个Coordinator来处理线程同步终止。此外,一个QueueRunner会运行一个_closer thread_,当Coordinator收到异常报告时,这个_closer thread_会自动关闭队列。

    您可以使用一个queue runner,来实现上述结构。
    首先建立一个TensorFlow图表,这个图表使用队列来输入样本。增加处理样本并将样本推入队列中的操作。增加training操作来移除队列中的样本。

    example = ...ops to create one example...
    # Create a queue, and an op that enqueues examples one at a time in the queue.
    queue = tf.RandomShuffleQueue(...)
    enqueue_op = queue.enqueue(example)
    # Create a training graph that starts by dequeuing a batch of examples.
    inputs = queue.dequeue_many(batch_size)
    train_op = ...use 'inputs' to build the training part of the graph...
    

    在Python的训练程序中,创建一个QueueRunner来运行几个线程, 这几个线程处理样本,并且将样本推入队列。创建一个Coordinator,让queue runner使用Coordinator来启动这些线程,创建一个训练的循环, 并且使用Coordinator来控制QueueRunner的线程们的终止。

    # Create a queue runner that will run 4 threads in parallel to enqueue
    # examples.
    qr = tf.train.QueueRunner(queue, [enqueue_op] * 4)
    
    # Launch the graph.
    sess = tf.Session()
    # Create a coordinator, launch the queue runner threads.
    coord = tf.train.Coordinator()
    enqueue_threads = qr.create_threads(sess, coord=coord, start=True)
    # Run the training loop, controlling termination with the coordinator.
    for step in xrange(1000000):
        if coord.should_stop():
            break
        sess.run(train_op)
    # When done, ask the threads to stop.
    coord.request_stop()
    # And wait for them to actually do it.
    coord.join(threads)
    

    异常处理

    通过queue runners启动的线程不仅仅只处理推送样本到队列。他们还捕捉和处理由队列产生的异常,包括OutOfRangeError异常,这个异常是用于报告队列被关闭。
    使用Coordinator的训练程序在主循环中必须同时捕捉和报告异常。
    下面是对上面训练循环的改进版本。

    try:
        for step in xrange(1000000):
            if coord.should_stop():
                break
            sess.run(train_op)
    except Exception, e:
       # Report exceptions to the coordinator.
       coord.request_stop(e)
    
    # Terminate as usual.  It is innocuous to request stop twice.
    coord.request_stop()
    coord.join(threads)
    
    展开全文
    Fighting_No1 2019-04-23 10:09:29
  • IIT后被美国国家仪器(NI,National Instruments)公司收购,软件更名为NI Multisim,Multisim经历了多个版本的升级,已经有Multisim2001、 Multisim7、 Multisim8、Multisim9 、Multisim10等版本,9版本之后增加了...

    Multisim模拟电路仿真

    本章Multisim10电路仿真软件,讲解使用Multisim进行模拟电路仿真的基本方法。
    目录

    1. Multisim软件入门

    2. 二极管电路

    3. 基本放大电路

    4. 差分放大电路

    5. 负反馈放大电路

    6. 集成运放信号运算和处理电路

    7. 互补对称(OCL)功率放大电路

    8. 信号产生和转换电路

    9. 可调式三端集成直流稳压电源电路

    13.1 Multisim用户界面及基本操作
    在这里插入图片描述
    13.1.1 Multisim用户界面
    在众多的EDA仿真软件中,Multisim软件界面友好、功能强大、易学易用,受到电类设计开发人员的青睐。Multisim用软件方法虚拟电子元器件及仪器仪表,将元器件和仪器集合为一体,是原理图设计、电路测试的虚拟仿真软件。
    Multisim来源于加拿大图像交互技术公司(Interactive Image Technologies,简称IIT公司)推出的以Windows为基础的仿真工具,原名EWB。
    IIT公司于1988年推出一个用于电子电路仿真和设计的EDA工具软件Electronics Work Bench(电子工作台,简称EWB),以界面形象直观、操作方便、分析功能强大、易学易用而得到迅速推广使用。
    1996年IIT推出了EWB5.0版本,在EWB5.x版本之后,从EWB6.0版本开始,IIT对EWB进行了较大变动,名称改为Multisim(多功能仿真软件)。
    IIT后被美国国家仪器(NI,National Instruments)公司收购,软件更名为NI Multisim,Multisim经历了多个版本的升级,已经有Multisim2001、 Multisim7、 Multisim8、Multisim9 、Multisim10等版本,9版本之后增加了单片机和LabVIEW虚拟仪器的仿真和应用。
    下面以Multisim10为例介绍其基本操作。图13.1-1是Multisim10的用户界面,包括菜单栏、标准工具栏、主工具栏、虚拟仪器工具栏、元器件工具栏、仿真按钮、状态栏、电路图编辑区等组成部分。

    图13.1-1 Multisim10用户界面
    菜单栏与Windows应用程序相似,如图13.1-2所示。

    图13.1-2 Multisim菜单栏
    其中,Options菜单下的Global Preferences和Sheet Properties可进行个性化界面设置,Multisim10提供两套电气元器件符号标准:
    ANSI:美国国家标准学会,美国标准,默认为该标准,本章采用默认设置;
    DIN:德国国家标准学会,欧洲标准,与中国符号标准一致。

    工具栏是标准的Windows应用程序风格。
    标准工具栏:
    视图工具栏:
    图13.1-3是主工具栏及按钮名称,图13.1-4是元器件工具栏及按钮名称,图13.1-5是虚拟仪器工具栏及仪器名称。

    图13.1-3 Multisim主工具栏

    图13.1-4 Multisim元器件工具栏

    图13.1-5 Multisim虚拟仪器工具栏

    项目管理器位于Multisim10工作界面的左半部分,电路以分层的形式展示,主要用于层次电路的显示,3个标签为:
    Hierarchy:对不同电路的分层显示,单击“新建”按钮将生成Circuit2电路;
    Visibility:设置是否显示电路的各种参数标识,如集成电路的引脚名;
    Project View:显示同一电路的不同页。
    13.1.2 Multisim仿真基本操作
    Multisim10仿真的基本步骤为:

    1. 建立电路文件
    2. 放置元器件和仪表
    3. 元器件编辑
    4. 连线和进一步调整
    5. 电路仿真
    6. 输出分析结果
      具体方式如下:
    7. 建立电路文件
      具体建立电路文件的方法有:
      打开Multisim10时自动打开空白电路文件Circuit1,保存时可以重新命名
      菜单File/New
      工具栏New按钮
      快捷键Ctrl+N
    8. 放置元器件和仪表
      Multisim10的元件数据库有:主元件库(Master Database),用户元件库(User Database),合作元件库(Corporate Database),后两个库由用户或合作人创建,新安装的Multisim10中这两个数据库是空的。
      放置元器件的方法有:
      菜单Place Component
      元件工具栏:Place/Component
      在绘图区右击,利用弹出菜单放置
      快捷键Ctrl+W
      放置仪表可以点击虚拟仪器工具栏相应按钮,或者使用菜单方式。
      以晶体管单管共射放大电路放置+12V电源为例,点击元器件工具栏放置电源按钮(Place Source),得到如图13.1-6所示界面。

    图13.1-6 放置电源
    修改电压值为12V,如图13.1-7所示。

    图13.1-7 修改电压源的电压值
    同理,放置接地端和电阻,如图13.1-8所示。

    图13.1-8 放置接地端(左图)和电阻(右图)
    图13.1-9为放置了元器件和仪器仪表的效果图,其中左下角是函数信号发生器,右上角是双通道示波器。

    图13.1-9 放置元器件和仪器仪表
    3. 元器件编辑
    (1)元器件参数设置
    双击元器件,弹出相关对话框,选项卡包括:
    Label:标签,Refdes编号,由系统自动分配,可以修改,但须保证编号唯一性
    Display:显示
    Value:数值
    Fault:故障设置,Leakage漏电;Short短路;Open开路;None无故障(默认)
    Pins:引脚,各引脚编号、类型、电气状态
    (2)元器件向导(Component Wizard)
    对特殊要求,可以用元器件向导编辑自己的元器件,一般是在已有元器件基础上进行编辑和修改。方法是:菜单Tools/ Component Wizard,按照规定步骤编辑,用元器件向导编辑生成的元器件放置在User Database(用户数据库)中。
    4. 连线和进一步调整
    连线:
    (1)自动连线:单击起始引脚,鼠标指针变为“十”字形,移动鼠标至目标引脚或导线,单击,则连线完成,当导线连接后呈现丁字交叉时,系统自动在交叉点放节点(Junction);
    (2)手动连线:单击起始引脚,鼠标指针变为“十”字形后,在需要拐弯处单击,可以固定连线的拐弯点,从而设定连线路径;
    (3)关于交叉点,Multisim10默认丁字交叉为导通,十字交叉为不导通,对于十字交叉而希望导通的情况,可以分段连线,即先连接起点到交叉点,然后连接交叉点到终点;也可以在已有连线上增加一个节点(Junction),从该节点引出新的连线,添加节点可以使用菜单Place/Junction,或者使用快捷键Ctrl+J。
    进一步调整:
    (1)调整位置:单击选定元件,移动至合适位置;
    (2)改变标号:双击进入属性对话框更改;
    (3)显示节点编号以方便仿真结果输出:菜单Options/Sheet Properties/Circuit/Net Names,选择Show All;
    (4)导线和节点删除:右击/Delete,或者点击选中,按键盘Delete键。

    图13.1-10是连线和调整后的电路图,图13.1-11是显示节点编号后的电路图。

    图13.1-10 连线和调整后的电路图

          (a)显示节点编号对话框                 (b)显示节点编号后的电路图
    

    图13.1-11 电路图的节点编号显示
    5.电路仿真
    基本方法:
    按下仿真开关,电路开始工作,Multisim界面的状态栏右端出现仿真状态指示;
    双击虚拟仪器,进行仪器设置,获得仿真结果

    图13.1-12是示波器界面,双击示波器,进行仪器设置,可以点击Reverse按钮将其背景反色,使用两个测量标尺,显示区给出对应时间及该时间的电压波形幅值,也可以用测量标尺测量信号周期。

    图13.1-12 示波器界面(右图为点击Reverse按钮将背景反色)

    1. 输出分析结果
      使用菜单命令Simulate/Analyses,以上述单管共射放大电路的静态工作点分析为例,步骤如下:
      菜单Simulate/Analyses/DC Operating Point
      选择输出节点1、4、5,点击ADD、Simulate

    图13.1-13 静态工作点分析

    13.2 二极管及三极管电路13.2.1 二极管参数测试仿真实验
    半导体二极管是由PN结构成的一种非线性元件。典型的二极管伏安特性曲线可分为4个区:死区、正向导通区、反向截止区、反向击穿区,二极管具有单向导电性、稳压特性,利用这些特性可以构成整流、限幅、钳位、稳压等功能电路。
    半导体二极管正向特性参数测试电路如图13.2-1所示。表13.2-1是正向测试的数据,从仿真数据可以看出:二极管电阻值不是固定值,当二极管两端正向电压小,处于“死区”,正向电阻很大、正向电流很小,当二极管两端正向电压超过死区电压,正向电流急剧增加,正向电阻也迅速减小,处于“正向导通区”。

    图13.2-1 二极管正向特性测试电路

    表13.2-1 二极管正向特性仿真测试数据
    Rw
    10%
    20%
    30%
    50%
    70%
    90%
    Vd/mV
    299
    496
    544
    583
    613
    660
    Id/mA
    0.004
    0.248
    0.684
    1.529
    2.860
    7.286
    rd=Vd/Id(欧姆)
    74750
    2000
    795
    381
    214
    90.58

    半导体二极管反向特性参数测试电路如图13.2-2所示。

    图13.2-2 二极管反向特性测试电路
    表13.2-2是反向测试的数据,从仿真数据可以看出:二极管反向电阻较大,而正向电阻小,故具有单向特性。反向电压超过一定数值(VBR),进入“反向击穿区”,反向电压的微小增大会导致反向电流急剧增加。
    表13.2-2 二极管反向特性仿真测试数据
    Rw
    10%
    30%
    50%
    80%
    90%
    100%
    Vd/mV
    10000
    30000
    49993
    79982
    80180
    80327
    Id/mA
    0
    0.004
    0.007
    0.043
    35
    197
    rd=Vd/Id(欧姆)

    7.5E6
    7.1E6
    1.8E6
    2290.9
    407.8

    13.2.2 二极管电路分析仿真实验
    二极管是非线性器件,引入线性电路模型可使分析更简单。有两种线性模型:
    (1)大信号状态下的理想二极管模型,理想二极管相当于一个理想开关;
    (2)正向压降与外加电压相比不可忽略,且正向电阻与外接电阻相比可以忽略时的恒压源模型,即一个恒压源与一个理想二极管串联。
    图13.2-3是二极管实验电路,由图中的电压表可以读出:二极管导通电压Von = 0.617V; 输出电压Vo = -2.617V。

    图13.2-3二极管实验电路(二极管为IN4148)
    利用二极管的单向导电性、正向导通后其压降基本恒定的特性,可实现对输入信号的限幅,
    图13.2-4(a)是二极管双向限幅实验电路。V1和V2是两个电压源,根据电路图,上限幅值为:V1+Von,下限幅值为:–V2–Von。在Vi的正半周,当输入信号幅值小于(V1+Von)时,D1、D2均截止,故Vo = Vi;当Vi大于(V1+Von)时,D1导通、D2截止,Vo = V1+Von≈4.65V;在Vi的负半周,当|Vi| < V2+Von时,D1、D2均截止,Vo = Vi;当|Vi| >(V2+Von)时,D2导通、D1截止,Vo = -(V2+Von)≈ -2.65V。图13.2-4(b)是二极管双向限幅实验电路的仿真结果,输出电压波形与理论分析基本一致。

    (a)二极管双向限幅仿真电路 (b)输出电压波形
    图13.2-4 二极管双向限幅实验电路
    13.2.3 三极管特性测试
    选择虚拟晶体管特性测试仪(IV-Analysis)XIV1,双击该图标,弹出测试仪界面,进行相应设置,如图13.2-5所示,点击Sim_Param按钮,设置集射极电压的起始范围、基极电流的起始范围,以及基极电流增加步数Num_Steps(对应特性曲线的根数),单击仿真按钮,得到一簇三极管输出特性曲线。
    右击其中的一条曲线,选择show select marts,则选中了某一条特性曲线,移动测试标尺,则在仪器界面下部可以显示对应的基极电流、集射极电压、集电极电流。根据测得的和值,可以计算出该工作点处的直流电流放大倍数,根据测得的和,可以计算出交流电流放大倍数。

    图13.2-5 用晶体管特性测试仪测量三极管特性
    13.3 单管基本放大电路13.3.1 共射放大电路仿真实验
    放大是对模拟信号最基本的处理,图13.3-1是单管共射放大电路(NPN型三极管)的仿真电路图。

    图13.3-1 单管共射放大电路(NPN型三极管)
    进行直流工作点分析,采用菜单命令Simulate/Analysis/DC Operating Point,在对话框中设置分析节点及电压或电流变量,如图13.3-2所示。图13.3-3是直流工作点分析结果。

    图13.3-2 直流工作点分析对话框

    图13.3-3 直流工作点分析结果
    当静态工作点合适,并且加入合适幅值的正弦信号时,可以得到基本无失真的输出,如图13.3-4所示。

    图13.3-4 单管共射放大电路输入输出波形
    但是,持续增大输入信号,由于超出了晶体管工作的线性工作区,将导致输出波形失真,如图13.3-5(a)所示,图13.3-5(b)是进行傅里叶频谱分析的结果,可见输出波形含有高次谐波分量。

    (a)输出波形失真 (b)傅里叶频谱分析结果
    图13.3-5 增大输入后的失真输出波形及其频谱分析结果
    静态工作点过低或者过高也会导致输出波形失真,如图13.3-6所示,由于基极电阻过小,导致基极电流过大,静态工作点靠近饱和区,集电极电流也因此变大,输出电压,大的集电极电流导致整个电路的输出电压变小,因此从输出波形上看,输出波形的下半周趋于被削平了,属于饱和失真。

    图13.3-6 减小Rb后的失真输出波形

    13.3.2 场效应管放大电路仿真实验

    1. 场效应管的转移特性
      场效应管的转移特性指漏-源电压固定时,栅-源电压对漏极电流的控制特性,即 ,按照图13.3-7搭建N沟道增强型场效应管转移特性实验电路,单击Multisim10 菜单“Simulate/Analyses/DC Sweep…”选择直流扫描分析功能,在弹出的对话框“Analysis Parameters”中设置所要扫描的直流电源,并设置起始和终止值、步长值,在“Output”选项卡中选择节点2的电压V[2]为分析节点,由于源极电阻,所以电压V[2]的数值等于源极电流,也等于漏极电流。由图13.3-7(b)可知,N沟道增强型场效应管2N7002的开启电压V。

    (a)仿真电路 (b)转移特性仿真结果
    图13.3-7 场效应管转移特性直流扫描分析
    2. 场效应管共源放大电路
    图13.3-8是场效应管共源放大电路仿真实验电路图,调整电阻和构成的分压网络可以改变,从而改变电压放大倍数。此外,改变电阻、也可改变输出电压。

    (a)仿真电路 (b)输入和输出电压波形
    图13.3-8 场效应管共源放大电路仿真
    13.4 放大电路指标测量13.4.1 输入电阻测量
    万用表可以测量交直流电压、交直流电流、电阻、电路中两个节点之间的分贝损耗,不需用户设置量程,参数默认为理想参数(比如电流表内阻为0),用户可以修改参数。点击虚拟仪器万用表(Multimeter),接入放大电路的输入回路,本例中将万用表设置为交流,测得的是有效值(RMS值)。由于交流输入电阻要在合适的静态工作点上测量,所以直流电源要保留。
    由图13.4-1可见,测得输入回路的输入电压有效值为3.536mV,电流为2.806μA,输入电阻。
    在实验室中进行的实物电路的输入电阻测量要采用间接测量方法,这是因为实际的电压表、电流表都不是理想仪器,电流表内阻不是0,而电压表内阻不是无穷大。

    (a) 输入电阻测量电路

    (b)电压、电流测量结果
    图13.4-1 放大电路输入电阻测量电路图
    13.4.2 输出电阻的测量
    采用外加激励法,将信号源短路,负载开路,在输出端接电压源,并测量电压、电流,如图13.4-2所示。
    由图13.4-2可见,测得输出回路的激励电压有效值为707.106mV,电流为517.861μA,输出电阻。

    (a)输出电阻测量

    (b)电压、电流测量结果
    图13.4-2 放大电路输出电阻测量电路图

    13.4.3 幅频特性的测量
    可以用示波器测量放大电路的增益,以电阻分压式共射放大电路为例,图13.4-3(a)是测量电压放大倍数的电路图,图13.4-3(b)是示波器输出波形。
    移动测试标尺可以读出输入输出波形幅值,进而计算出电压放大倍数,但是,可以发现,标尺处于不同位置计算出的结果不同,仅可作为估计值,此外,输出波形与输入波形相比,存在一定相移,不是理想的反相,即发生了相移,相移大小与频率有关,这就是该放大电路的相频特性。
    除了用示波器进行放大倍数测量的方法。还有两种方法:扫描分析法和波特仪测量法。

    (a) (b)
    图13.4-3 分压式共射放大电路放大倍数的测量

    1. 扫描分析法
      由菜单Simulate/Analyses/AC Analysis,弹出AC Analysis(交流分析)对话框,如图13.4-4所示,选项卡Frequency Parameters中设置Start frequency(起始频率,本例设为1Hz)、Stop frequency(终止频率,本例设为10GHz)、Sweep type(扫描方式,本例设为Decade,十倍频扫描)、Number of points per decade(每十倍频的采样点数,默认为10)、Vertical scale(纵坐标刻度,默认是Logarithmic,即对数形式,本例选择Linear,即线性坐标,更便于读出其电压放大倍数)。
      在Output选项卡中选择节点5的电压V[5]为分析变量,按下Simulate(仿真)按钮,得到图13.4-4(b)所示的频谱图,包括幅频特性和相频特性两个图。
      在幅频特性波形图的左侧,有个红色的三角块指示,表明当前激活图形是幅频特性,为了详细获取数值信息,按下工具栏的Show/Hide Cursors按钮,则显示出测量标尺和数据窗口,移动测试标尺,则可以读取详细数值,如图(c)和(d)所示。同理,可激活相频特性图形,进行相应测量。

    (a)AC Analysis对话框 (b)被分析节点的幅频和相频特性

    (c) 用测试标尺读取详细数值 (d)频响数据
    图13.4-4 扫描分析法进行放大电路幅频特性测量
    2. 波特仪测量法
    波特仪(Bode Plotter)也称为扫频仪,用于测量电路的频响(幅频特性、相频特性),将波特仪连接至输入端和被测节点,如图13.4-5(a)所示,双击波特仪,获得频响特性,图13.4-5(b)是幅频响应,图13.4-5(c)是相频响应。

    (a)波特仪测试频响电路图

    (b)幅频特性测试结果

    (c)相频特性测试结果
    图13.4-5 扫描分析法进行放大电路幅频特性测量
    波特仪的面板设置:
    (1)Mode:模式选择,点击Magnitude获得幅频响应曲线,选择Phase获得相频响应曲线;
    (2)水平和垂直坐标:点击Log选择对数刻度,点击Lin选择线性刻度;
    (3)起始范围:F文本框内填写终了值及单位,I文本框内填写起始值及单位。
    13.5 差动放大电路13.5.1 差动放大电路仿真电路
    直接耦合是多级放大的重要级间连接方式,对直流信号、变化缓慢的信号只能用直接耦合,但随之而来的是零点漂移问题,影响电路的稳定,解决这个问题的一个办法是采用差动放大电路,在电子设备中常用差动放大电路放大差摸信号,抑制温度变化、电源电压波动等引起的共模信号。
    图13.5-1是差动放大电路仿真电路,是由两个相同的共射放大电路组成的,当开关J1拨向左侧时,构成了一个典型的差动放大电路,调零电位器Rw用来调节Q1、Q2管的静态工作点,使得输入信号为0时,双端输出电压(即电阻RL上的电压)为0。
    当开关J1拨向右侧时,构成了一个具有恒流源的差动放大电路,用恒流源代替射极电阻Re,可以进一步提高抑制共模信号的能力。
    差动放大电路的输入信号既可以是交流信号,也可以是直流信号。图13.5-1中,输入信号由函数发生器提供,函数发生器(Function Generator)可以产生正弦波、三角波、矩形波电压信号,可设置的参数有:频率、幅值、占空比、直流偏置,频率范围很宽(0.001pHz~1000THz)。
    差动放大电路需要一正一负两个电压源,实际中不存在负的电压源,将正极接地,则电压源的负极可以提供负的电压,因此,按照图中的接法可以提供正负电压源。
    差动放大电路有两个输入端和两个输出端,因此电路组态有双入双出、双入单出、单入双出、单入单出4种,凡是双端输出,差摸电压放大倍数与单管情况下相同,凡是单端输出,差摸电压放大倍数为单管情况下的一半。

    图13.5-1 差动放大电路仿真电路
    13.5.2 差动放大电路的调零
    调零是指差动放大器输入端不接入信号,调整电路参数使两个输出端达到等电位。
    图13.5-2中是调整电位器Rw,使节点3和节点4的电压相同,这时可认为左右两侧的电路已经对称,调零工作完成。
    图中的电压读数也是两个三极管的集电极静态工作电压。

    图13.5-2 差动放大电路的调零
    13.5.3 差动放大电路的静态工作点
    采用菜单命令Simulate/Analysis/DC Operating Point,选择节点仿真可以获得静态工作点指标,下面采用另一种方法,将电流表和电压表接入仿真电路,获得更直观的静态工作点测量结果,如图13.5-3所示。

    图13.5-3 差动放大电路的静态工作点测量
    13.5.4 差模增益和共模增益测量

    1. 差模电压增益
      双端输入双端输出情况下的差摸电压放大倍数是输出端电压差除以输入端电压差。
      为获得较大电压增益,将仿真电路的参数进行一些调整,测量电路如图13.5-4所示。
      函数发生器设置为输出正弦波,频率1kHz,幅值5mV,“+”端和“-”端接入差动放大电路的两个输入端,COM端接地。
      用电压表测量输入端的电压差,注意双击电压表,将测量模式(Mode)改为交流(AC)模式。
      由图中测量数据,输入端电压差为7.071mV,输出端电压差为308.991mV,双入双出模式时的差摸电压增益为。
      当开关J1拨向右侧时,以恒流源代替射极电阻,则差摸电压增益增加到。
      仿真可发现,负载电阻RL对增益值影响很大,此外,调零电阻Rw、基极电阻Rb1、Rb2、集电极电阻Rc1、Rc2均有影响。

    图13.5-4 双入双出差动放大电路的差摸增益测量

    1. 共模电压增益
      将两输入端短接,COM端接地,构成共模输入方式,如图13.5-5所示。
      调整输入信号频率为1kHz,幅值为1mV,在负载电阻两端接万用表,测得输出电压为6pV左右,“皮”的数量级为10-12,几乎为零。可见,差动放大电路对共模信号有很强的抑制效果。

    图13.5-5 双入双出差动放大电路的共摸增益测量
    13.6 集成运放电路
    由分立元件构成的电路具有电子设计上灵活性大的优点,但缺点是功耗大、稳定性差、可靠性差,此外,设计本身较复杂。集成电路采用微电子技术构成具有特定功能的电路系统模块,与分立元件构成的电路相比,性能有了很大提高,电子设计也更为简单。
    集成运算放大器是高增益、高输入阻抗、低输出阻抗、直接耦合的线性放大集成电路,功耗低、稳定性好、可靠性高。可以通过外围元器件的连接构成放大器、信号发生电路、运算电路、滤波器等电路。
    以集成运放μA741为例,图13.6-1是μA741的管脚示意图及实物照片。

    图13.6-1 集成运放μA741管脚示意图及实物照片

    13.6.1 比例放大电路
    用μA741组成同相比例放大电路,仿真电路图如图13.6-2所示。根据同相比例电路的增益公式,图13.6-2的电压增益应为:。

    (a)同相比例放大电路

    (b)输入、输出电压波形
    图13.6-2 集成运放μA741构成的同相比例放大电路
    从波形上看,输入、输出同相位,用测试标尺测量幅值,可发现输出与输入的比例为3,在一定范围内调整负载电阻,波形基本不变,说明该电路带负载能力强。同理,可以进行反相比例放大电路的仿真,图13.6-3是集成运放μA741构成的反相比例放大电路,其电压增益应为:,这与示波器读数一致。

    图13.6-3 集成运放μA741构成的反相比例放大电路及波形
    由仿真可见,由运算放大器构成比例放大电路的电路结构简单、设计容易、性能稳定、带负载能力强。
    13.6.2有源滤波电路
    根据滤波电路中有无有源元件可将滤波器电路分为无源滤波器和有源滤波器,无源滤波器由无源元器件(电阻、电容、电感)构成电路网络,但其滤波特性随着负载的变化而变化,负载效应明显,不能满足很多应用场合的要求,有源滤波器则通过运放电路提高输入阻抗,降低输出阻抗而大大减少了负载效应。
    简单的有源滤波器是在无源滤波器输出端接一个由运放电路构成的电压跟随器或同相比例放大器,使得滤波的同时可以放大信号,并且提高带负载能力。
    图13.6-4是简单的二阶低通有源滤波电路,运放U1和电阻Rf、R3构成同相比例放大电路,放大倍数为,电阻R1、电容C1、电阻R2、电容C2组成的RC网络是二阶低通滤波电路,其特征频率为Hz。信号源是幅值为1V的交流电压源。
    用菜单命令Simulate/Analyses/AC Analysis对其进行交流分析,频率范围设置为1Hz~1MHz,扫描类型Sweep type选择Decade,纵坐标Vertical Scale选择Linear,Output选项卡中选择节点4作为分析节点,单击Simulate按钮,可得到其频率特性,如图13.6-5所示。

    图13.6-4 简单二阶低通有源滤波电路
    由频率特性可以看出:最大输出为1.9996V,截止频率为对应V(即增益下降3dB)的频率,约为125.4003Hz(标尺2处),而在特征频率处(标尺1处,338.2989Hz),幅值已下降至672.8329mV,可见,实际的截止频率远小于特征频率。为缩小二者的差别,可引入正反馈增大特征频率处的幅值,这就是所谓的压控电压源二阶低通滤波器。

    图13.6-5简单二阶低通有源滤波电路的频率特性

    将电容C1的下端直接接在滤波器输出端,构成图13.6-6所示的压控电压源二阶低通滤波器,其频率特性如图13.6-7所示。

    图13.6-6 压控电压源二阶低通滤波电路

    图13.6-7 压控电压源二阶低通滤波电路的频率特性
    可以看出,特征频率处的幅值有所增大,在特征频率处(测量标尺1,338.2989Hz)幅值增大为1.9857V,截止频率为1.414V所对应的频率,在测量标尺2处(幅值为1.3912),对应频率为439.2605Hz,二者差距由约213Hz缩小至约100Hz,特征频率和截止频率差距大大缩小了。
    品质因数Q的物理意义是特征频率处的电压增益与通带电压增益之比,理论分析给出品质因数Q与通带增益的关系为:,而在本节例子中,通带增益,因此,改变运放电阻或者即可改变品质因数。
    13.7 直流稳压电源13.7.1 桥式整流滤波电路
    建立如图13.7-1所示的单相桥式整流滤波电路,变压器取值Basic Group组的BASIC_VIRTUAL中的TS_VIRTUAL,设置变比(本例设为10),变压器的二次侧有3个抽头,可以有两种接法,如图13.7-1中的(a)和(b)所示,前者的整流波形最大值约为15V,后者约为30V,整流桥选自Diodes组中的FWB中的元件。

    (a)变压器输出15V整流波形

    (b)变压器输出30V整流波形
    图13.7-1 单相桥式整流滤波电路
    以图13.7-1(b)电路为例,图13.7-2是该单相桥式整流滤波电路的输出波形,图(a)是未接入滤波电容C1时的输出波形,即整流桥输出波形,图(b)是接入滤波电容C1时的输出波形,可见,桥式整流后用滤波电容进行滤波,电压平均值上升,电压波动(波纹系数)减小了。
    但是,RC回路参数对波形影响很大,波形与滤波电容的大小有关系,也与负载大小有关系。将负载增至10kΩ,输出波形如图13.7-2(c)所示,可见输出电压的波动进一步减小,若继续将滤波电容增至100μF,则电压波形趋于理想,如图13.7-2(d)所示。
    当负载较轻(对应负载电阻大),对电压波形要求不高时,可采用这种方式提供直流电压,为减少纹波系数,可适当增大滤波电容。

    (a)未接入滤波电容C1时的输出波形 (b)接入滤波电容C1时的输出波形

    (c)电容为47μF、负载为10kΩ时的输出波形 (d)电容为100μF、负载为10kΩ时的输出波形
    图13.7-2 单相桥式整流滤波电路的输出波形
    13.7.2 串联线性稳压电路
    串联稳压是指稳压元件(调整三极管)与负载串联的稳压电路,图13.7-3是串联线性稳压电路,稳压管取自Diodes组的DIODES_VIRTUAL中的ZENER_VIRTUAL,可修改稳压值;调整三极管的选择要确保最大耗散功率满足要求(一般不小于2W),并保证电流输出能力(对应最小输出电压);取样电阻取千欧级以降低功耗。

    图13.7-3 串联线性稳压电路

    图13.7-4是串联线性稳压电路的输入、输出波形,示波器上部的波形是串联稳压电路输入电压信号,可见存在电压纹波,下部的波形是串联稳压电路的输出电压信号,几乎是理想的直流电压。
    调整取样电位器,可以调整输出电压的幅值,获得一定可调范围的直流输出电压。

    Multisim入门到精通视频教程:如果需要请私信我

    展开全文
    weixin_44212493 2020-02-15 17:29:18
  • 5星
    10.42MB qijianjiang 2015-06-28 20:29:08
  • [分享交流]145直接升级鸿蒙会不会掉资料18919电梯直达中二的灵魂略有小成发表于 2020-12-19 06:58:28来自:HUAWEI Mate 30 5G最新回复 2020-12-19 10:59:19如题,有升了的老哥说说吗?是直接好像升级那样ota吗想着你...

    [分享交流]

    145直接升级鸿蒙会不会掉资料

    18919

    电梯直达

    34140ae382b34aa0d5801382ef8e3a0f.png

    4e8fd6166c949a0de3eb0564ca8c8777.png

    中二的灵魂

    略有小成

    发表于 2020-12-19 06:58:28

    来自:HUAWEI Mate 30 5G

    最新回复 2020-12-19 10:59:19

    如题,有升了的老哥说说吗?是直接好像升级那样ota吗

    57844bd38c2ec3ce996b79d1cde5dcd5.png

    想着你的狼

    自成一派

    发表于 2020-12-19 07:03:02

    来自:HUAWEI Mate 30 5G

    半年以后能升级就不错了,有点操之过急了!

    852f345a7c1ec136d9c3455464e6c7e5.png

    慧剑有泪

    1fd2281de729c3a82a6d97dd8a2fba32.png

    热心花粉

    发表于 2020-12-19 07:13:43

    来自:HUAWEI Mate X 5G

    一般来说升级是不会丢失资料,只有降级会,不过备份一下还是最好

    f9e1a612d5ddbe09376cb497a518f5ab.png

    右边的角

    独步江湖

    发表于 2020-12-19 07:50:44

    来自:HUAWEI Mate 30 Pro 5G

    升级不会丢资料,但你要是想回退必定要清空系统了

    6c4e86c5e6c8a8109ff0392c8252b7fd.png

    Q莫陌Q

    自成一派

    发表于 2020-12-19 08:15:49

    来自:HUAWEI Mate 30

    想多了

    b621ab442bb5861c7e870f1d48c70a25.png

    YoungcyChu

    独步江湖

    发表于 2020-12-19 09:00:28

    来自:HUAWEI Mate 30 Pro 5G

    估计那会儿186都出了

    7ce54d3d9e6da8ccef39854e419d93e5.png

    7ce54d3d9e6da8ccef39854e419d93e5.png

    7ce54d3d9e6da8ccef39854e419d93e5.png

    1da95852ebf6f6820147351ed65bc4a1.png

    xymsayly

    略有小成

    发表于 2020-12-19 09:06:23

    来自:HUAWEI Mate 30 Pro

    你这想的有点早了,还早呢

    1b554bb768c70ac08a75550f12c1b7cd.png

    禾木与否

    独步江湖

    发表于 2020-12-19 09:09:23

    来自:浏览器

    升级是覆盖不会丢资料的,但是降级会清楚数据就会丢资料了。

    79c36b9d49c30f0a7e947386ae9d33ae.png

    BH8AVW

    1fd2281de729c3a82a6d97dd8a2fba32.png

    花粉版主

    发表于 2020-12-19 09:30:32

    来自:浏览器

    请注意:

    你报名的仅仅是HarmonyOS 2.0手机开发者Beta公测版,此版本是面向手机开发者的版本,请问你是手机开发者吗?      目前华为已经开放完整的 HarmonyOS 2.0 系统能力、丰富的 API(应用开发接口),以及强大的开发工具 DevEco Studio 等技术装备,开发者可访问华为开发者联盟官网,申请获取 HarmonyOS2.0 手机开发者 Beta 版升级。

    友情提醒,如果你不是手机开发者,请不要继续跟进本次开发者Beta公测版!这个版本不是给普通用户推送的!

    学会尊重他人,就是尊重自己;学会欣赏他人,就是欣赏自己;学会呵护他人,才是呵护自己。

    4e8fd6166c949a0de3eb0564ca8c8777.png

    中二的灵魂

    略有小成

    发表于 2020-12-19 10:59:19

    来自:HUAWEI Mate 30 5G

    是开发,还没放出所以问下,谢谢版主

    女神节专属勋章

    女神节专属勋章(仅限2021年女神节期间领取,活动现已结束)

    华为新一代折叠屏Mate X2专属勋章

    华为Mate X2旗舰启航,仅限新品发布会期间领取(活动已结束),以及华为Mate X2用户领取(活动持续进行中)

    1024程序员节纪念勋章

    We are committed to innovate for all mankind, no matter if rain or shine -致敬用技术改变世界的程序员们

    国庆-中秋双节纪念勋章

    即日起至10月8号,国庆-中秋双节期间可领取此勋章

    华为开发者大会纪念勋章

    9月10号至9月13号,华为开发者大会期间可领取此勋章

    花粉俱乐部8周年

    花粉俱乐部8周年纪念勋章,新用户前往任务中心完成首帖任务即可领取

    华为Mate30系列

    华为Mate30系列机型专属勋章

    邀你看大片

    嘉年华活动限定勋章,积分达到50可获得

    超级吃货节

    嘉年华活动限定勋章,积分达到50可获得

    头号游戏迷

    嘉年华活动限定勋章,积分达到50可获得

    好机友勋章

    花粉好机友,注册时间大于99天

    五一勋章

    劳动最光荣,签到不能断!这个五一假期,我们也要多多“劳动”!连续签到三天,领取专属的五一勋章!

    金牛贺岁勋章

    春节纪念勋章,仅限牛年春节期间连续签到3天领取(活动现已结束)

    连续签到7天

    连续签到7天可获得此勋章

    花粉帖王

    发表100个主题帖即可获得

    大富翁勋章

    达到1万花瓣后可申请获得大富翁勋章

    2020年度技术花粉黄金勋章

    2020年度技术花粉黄金勋章

    C位担当

    粉丝数达50可获得此勋章

    花粉捧场王

    关注数达50可获得此勋章

    技术花粉专业等级

    技术积分达到30分时可获得此勋章

    HUAWEI Mate 20 X (5G)

    HUAWEI Mate 20 X (5G)专属勋章

    技术花粉专业等级

    技术积分达到6分时可获得此勋章

    热心花粉

    热心花粉用户组专属勋章

    玩机超神

    花粉ID至少20个原创技术帖,每个帖≥15K浏览,有效回复数≥1500

    玩机超人

    花粉ID至少10个原创技术帖,每个帖≥10K浏览,有效回复数≥1000

    玩机达人

    花粉ID至少5个原创技术帖,每个帖≥5K浏览,有效回复数≥500

    猴年勋章

    猴年专属纪念勋章

    今日头条

    参与今日头条相关活动获得奖励

    新浪微博

    关注@华为花粉俱乐部 新浪微博

    主题分享狂人

    主题资源合集不得少于8个(合集内资源量不得少于10个);主题资源帖不得少于80个;每个主帖不少于30张壁纸

    主题分享达人

    主题资源合集不得少于3个(合集内资源量不得少于10个);主题资源帖不得少于30个;每个主帖不少于30张壁纸

    主题分享新星

    主题资源合集不得少于1个(合集内资源量不得少于10个);主题资源帖不得少于15个;每个主帖不少于30张壁纸

    游戏分享狂人

    游戏资源合集不得少于8个(合集内资源量不得少于10个);游戏资源帖不得少于80个;游戏资源下载总量不低于5000

    游戏分享达人

    游戏资源合集不得少于3个(合集内资源量不得少于10个);游戏资源帖不得少于30个;游戏资源下载总量不低于2000

    游戏分享新星

    游戏资源合集不得少于1个(合集内资源量不得少于10个);游戏资源帖不得少于15个;游戏资源下载总量不低于1000

    应用分享狂人

    应用资源合集不得少于8个(合集内资源量不得少于10个);应用资源帖不得少于80个;应用资源下载总量不低于5000

    应用分享达人

    应用资源合集不得少于3个(合集内资源量不得少于10个);应用资源帖不得少于30个;应用资源下载总量不低于2000

    评测超人

    至少5个原创评测帖,每个帖≥8K浏览,有效回复≥300,加分数达≥25

    评测牛人

    至少3个原创测评帖,每个帖≥5K浏览量,有效回复≥150,并且加分数达≥15

    技术超人

    至少5个原创技术帖,每个帖≥8K浏览,有效回复≥300,加分数达≥25

    特种部队

    花粉特种部队荣耀勋章

    技术牛人

    至少3个原创技术帖,每个帖≥5K浏览,有效回复数≥150,被加分数≥15

    花粉版主

    花粉版主的身份荣誉勋章,感谢版主的辛勤付出

    评测神人

    至少10个原创评测帖,每个帖≥1W2浏览,有效回复≥600,加分数达≥50

    技术神人

    至少10个原创技术帖,每个帖≥1W2浏览,有效回复≥600,加分数达≥50

    运动教练

    官方认证专业运动教练

    2020年度技术花粉钻石勋章

    2020年度技术花粉钻石勋章

    技术花粉专业等级

    技术积分达到1800分时可获得此勋章

    花粉帖仙

    发表1000个主题帖即可获得

    精华帖贡献达人

    发表超过15个精华帖数

    技术花粉专业等级

    技术积分达到600分时可获得此勋章

    技术花粉专业等级

    技术积分达到150分时可获得此勋章

    花粉帖皇

    发表500个主题帖即可获得

    展开全文
    weixin_42298809 2021-05-29 13:16:41
  • u012932409 2021-06-16 15:31:34
  • KaiKaiWaWa 2021-11-05 20:43:42
  • qq_29350001 2019-11-20 18:41:48
  • xjhhjx 2018-04-12 08:05:12
  • weixin_43133008 2019-08-27 22:55:59
  • weixin_36396810 2021-05-07 07:27:07
  • wzj_what_why_how 2021-05-04 14:05:53
  • weixin_30349167 2020-12-31 05:25:45
  • dsp1406790497 2020-07-26 21:39:46
  • weixin_32445895 2021-06-23 01:24:21
  • weixin_36158842 2021-05-15 22:56:51
  • weixin_29421255 2021-07-17 00:39:54
  • zhxdick 2021-04-04 08:32:27
  • xuduorui 2017-10-18 22:32:30
  • shouqian_com 2016-09-13 13:18:48
  • weixin_32431067 2021-08-04 07:21:43
  • HermitSun 2020-05-28 10:26:14
  • talk_8 2021-11-28 20:21:45
  • qq_40421919 2019-06-12 15:28:34
  • weixin_39608132 2021-01-19 11:28:50
  • u011046184 2019-05-25 10:37:06
  • fengge2018 2020-09-22 03:20:02
  • weixin_44585583 2019-05-30 20:24:13

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 18,874
精华内容 7,549
关键字:

双q升级