分类

类型:
不限 游戏开发 计算机程序开发 Android开发 网站开发 笔记总结 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
原创:
不限
年份:
不限 2018 2019 2020

技术文章列表

  • 把修改/更新过的项目重新提交至github上

    更新项目提交至github只需要几条命令即可:

    在本地的git仓库将你修改过的项目复制到下面(覆盖掉之前上传的)
    右击选择Git Bash Here打开命令行
    输入下面四行命令即可

    git statusgit add . (别忘了add后面+空格+.)git commit -m “备注”git push origin master

    最后刷新下就OK了。
    1 留言 2019-05-13 16:54:19 奖励2点积分
  • 5G将渗透哪些领域

    5G爆发前夕将渗透哪些领域?5G 已经被吹捧为许多细分市场的新的赋能者,它将给移动电话、自动驾驶、虚拟现实(VR)和物联网等行业带来机会。但是,这个新的无线通信标准将在何时和将以何种方式影响这些细分市场,以及它将对半导体设计产生什么样的影响,目前尚有很多问题和不确定性。
    基于 5G 承诺的通信速度的大幅度提高和延迟的大幅度降低,系统供应商必须在将数据处理放在本地或在云端之间作出决定。这将对半导体体系结构产生重大影响,包括处理器和内存的片上吞吐量、I/O 速度、功率预算,甚至电池大小等等。此外,这些决定还将受到 5G 基础设施接入和通信频率的影响。
    不过,这些事情可能要等到数年后才会发生。一开始,大部分的 5G 应用将在低于 6GHz 的范围内,也就是和 4.5G 相当。最大的受益者将是移动电话行业,它将在一段时间内仍然是 5G 技术的最大消费者。移动电话的标准和存储容量将会因为 5G 技术的不断引入而持续进化,它们将依然是 5G 技术的发展的重要资金来源。
    下一个阶段的发展是在毫米波技术引进之时。这时候最重要的变化将开始发生。一般的经验法则是,任何一项新技术想要成功,必须提供10倍的增益,要么是性能提高、或功率降低、成本降低、面积更小或者多种增益的组合。只有在那个时候,5G 技术才会真正地大放光彩。
    “5G 将在连接性能方面提供显著的改进,其目标是比目前 4G 的连接性能提高1000倍”。Steven Woo 说,他是 Rambus 实验室负责系统和解决方案的副总裁、杰出的发明家。 “除了带宽方面的改进,5G 还承诺了降低延迟和更好的覆盖范围。”
    在毫米波技术的应用初期,设计成本和硅晶元出货面积将会显著上升。电力消耗将成为主要的问题,这取决于基础设施和它能承受的负荷压力,因为它们需要承担信号发送和信号到达时的计算工作。
    物联网一个真正能从 5G 中受益的领域是边缘计算,其中功耗是一个限制因素。“我们希望 5G 的引入能使物联网边缘设备的功耗更低,因为平均来说 5G 拉近了它们与接入点的距离。” Cadence 的产品营销总监 Neil Robinson 说:“这意味着与 4G 通信所需要的更长距离相比,5G 通信所需要的功耗更低”。
    这样就打开了一扇大门,让我们有能力处理比目前更加复杂的处理流程和通信方案。Woo 说:“5G 将提供更高的带宽,这意味着越来越多的终端(endpoint)将会更容把它们的数据传输到相邻位置,在那里这些数据得以在本地处理。”这意味着只有少量的,级别更高的数据/信息,才需要传输到云端处理。
    但这种方案也会很快变得复杂。“5G 所需的高带宽和多天线策略意味着,从本质上讲,它们需要更高的能耗。”西门子仿真部门市场营销高级总监 Jean-Marie Brunet 指出。“然而,人们普遍认为物联网的大部分将是机对机(M2M)通信。而机对机通信模式比人工启动的物联网更容易预测,因此机对机实例的低功耗算法应该更高效,这种说法是有争议的。”
    实际上的好处将随着不同的应用和区域有所不同。“归根结底,5G 将使每比特功耗降低10倍到100倍,同时带来相当于10倍的带宽增幅,这些变化将在元器件的寿命方面产生净增益,并同时提高10倍的功效。因使用模式的不同这些好处会有所不同。”
    这是否足以影响架构?物联网边缘设备已开始将推理运算本地化,以避免与向云端传输大量原始数据而带来的带宽影响。
    “更高的带宽和更低的延迟将使在云端进行推理运算变得更容易,如果我们需要这样做的话。” Cadence 的 Robinson 说。“但是,隐私、安全、延迟和功耗问题可能会让这种方式变得不合适。”
    Cadence 的 Tensilica IP 产品管理和营销高级总监 Lazaar Louis 也回应了这些担忧。“对隐私和安全问题的担忧将会使推理运算继续留在边缘设备进行,”他说。“将传感器收集到的信息传送到云端会消耗能量,因此在边缘设备进行推理运算,可以节省在边缘设备上消耗的能量。”
    Rambus 实验室的 Woo 同意这一说法。“更高的带宽可能不能排除在边缘设备上进行推理运算的必要性,但它们将允许更大数量的物联网设备相互连接,使基础设施能够跟上不断增长的物联网设备的需求,并使其捕获和传输的数据量不断增长。”
    VR(虚拟现实)和AR(增强现实)虚拟现实(VR)碰到了难题。如果没有更高的数据速率,设备供应商将很难消除晕眩不适感,这大大地限制了 VR 的使用。毫米波技术可以帮助解决这个问题。虽然毫米波信号不能通过墙壁,只能在相对较短的距离内工作,但 VR 耳机和控制器的距离通常只有几英尺远。
    “与以前运行同一款游戏的 4G 产品相比,高分辨率的 8K 游戏流媒体肯定会更快地将电池的电量耗光。” Brunet 承认 “但是这并不是 5G 造成的,而是因为更高级的 CPU 和显示设备。在毫米波频率下,对于空中下载(OTA)的功耗需求,收发器的功耗将会以加速度级地增加。”
    自动驾驶自动驾驶需要将许多技术结合在一起,5G 就是其中之一。“更低的延迟为具有自主驾驶能力的联网车辆带来了好处,在这种情况下,响应时间至关重要,尤其是在高速公路的行驶速度下。”Woo说道。“5G 覆盖范围的改进,加上增强的本地处理能力,将允许在数据产生的终端设备附近对数据进行聚合和处理,从而减少数据传输。减少长距离的数据传输对于 5G 来说是一个重要的好处,因为它可以同时改进了延迟和功耗。”
    汽车很可能成为通讯枢纽。“汽车将是微型发射器,” Robinson 说。“和现在的 802.11p(V2V)和4G/OnStar(V2X)面临的情况类似,5G 也面临着来自电视网络的阻挠,他们希望使用已经分配给 V2V(车辆对车辆)的相同频段。他们声称,自动驾驶不需要 V2V,在路上使用 V2X(车辆对所有)就可以来了解其他车辆的存在/意图。”
    这个观点得到其他人的同意。“车辆到所有(V2X)将主导通信,车辆将成为拥有很多发射器的移动网络,在许多情况下,每个功能域都有多个发射器。” Brunet 说:“V2X 将是实现安全的关键因素,因为激光雷达或雷达根本看不到拐角处,而 5G 可以依据拐角处的反射将这种实现变得可能。”
    到底是从其他车辆还是从路边的信息中得到这些信息,现在尚没有明确的答案。“汔车能获得的信息越多,它们就越能在自动驾驶体验方面做出更好的决定。” Cadence 公司的 Louis 指出。 “更先进的自动驾驶将得益于和其他车辆/基础设施的 V2X 通信,例如路线规划和车道变更辅助。”
    新的通信能力也可能带给我们今天还没有想到的好处。“联网车辆只是众多可能受益的设备之一。” Woo 说。“更高的带宽和更高的覆盖率,再加上更强的本地处理能力,将帮助车辆与周围环境以及本地地图数据进行通信,从而在将来实现道路导航。今天的联网汽车已经捕获了大量的数据,其中一些信息被传送到云端。预计汽车将继续发展而成为信息枢纽,因为它们可以作为乘客设备的连接点(就像手机可以作为智能手表和健身设备等外围设备的连接点一样),并与其他车辆进行点对点通信,以交流像车道变化之类的预期行动,以及交通状况和危险信息。”
    实施注意事项今天大多数 5G 的实现都还只是原型,而且并不是所有的问题都已经得到解决。例如,5G 可以提供每秒10到20千兆位的传输速度,但数字系统必须具备相应的工作速度才能受益于 5G 的高速度。如果为了降低成本而仍然使用旧的工艺节点生成数字信息,这可能使得能使用的工艺节点变得有限,或者我们需要更先过的封装解决方案。
    “这是摩尔定律的一个结合,它减慢了速度,同时增加了芯片的复杂性和所需要的工艺的复杂性。。” Rambus 的产品管理高级主管 Frank Ferro 表示:“你不需要做一个大型的 ASIC,你必须问一下分解它是否更划算。做两个更小的 ASIC 或重新使用你已经进行了大量投资的混合信号器件是否更便宜?如果你已经在高速工艺技术上进行了投资,你想继续扩大规模吗?或者,您可以使用现有的技术,加上接口技术,而不必每次更改工艺节点时都开发 SerDes 呢?“
    同样,不同的产品领域可能会得出不同的结论。“我不知道 5G 节点的期望值。” Robinson 说道。“这将归结为每个产品或公司的成本
    2 留言 2019-04-22 16:18:28 奖励15点积分
  • python爬虫--爬取网站中的多个网页

    爬取7k7k小游戏的URL
    # -*- coding: utf-8 -*-"""Created on Sun Mar 24 10:04:58 2019@author: pry"""import requestsfrom bs4 import BeautifulSoupimport osimport reimport urllibfrom lxml import etreedef parse_page(): t = 1 headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3642.0 Safari/537.36' } for i in range(1,5): url_i = 'http://www.7k7k.com/flash_fl/461_' + str(i) + '.htm' response_i = requests.get(url_i, headers = headers) selector = etree.HTML(response_i.text, parser=etree.HTMLParser(encoding = 'utf-8')) print(url_i) content = selector.xpath('//a/@href') for i in content: if i[0] == "j": continue if i[0] == "/": i = url_i + i with open('7k7k_urls.txt','a+') as file: file.write(i) file.write("\n") file.close() print(i) t = t + 1 print(t) print('ok')if __name__ == '__main__': parse_page()
    2 留言 2019-04-25 09:25:26 奖励5点积分
  • Android Studio使用POI读取及修改Word文档(.docx格式)

    一、说明上一篇文章(Android Studio使用POI读取及修改Word文档(.doc格式))使用poi对.doc格式的word文档进行了读取和更改,但很多情况下还需要在word文档中插入图片,这时就需要对.docx格式的word进行操作了。
    二、实现过程2.1 制作文书文书在源代码中可以直接看到,简单说明一下:文书有普通字段、表格、特定位置的图片,又在页眉页脚中加了普通字段和表格,基本满足对于word操作的所有情况。

    2.2 导包还是上篇中poi-3.9压缩资源包中的jar包,对.docx格式文档的操作用到XWPFDocument方法,使用到所有ooxml相关的jar包。

    2.3 build配置这次的build配置有点特殊,特别拿出来截图一下。就像上一篇说的一样,apache的很多配置在安卓是跑不通的,这次导包后,你会遇到方法过多,文件重复,基于jdk1.6以上版本的变异保存等一系列问题,可以按照下面的方法处理。当然不同的android studio版本可能也会有不同的处理方法,可以百度一下。

    权限还是储存权限,直接上代码吧,注解的也很详细。
    2.4 实现源码public class MainActivity extends AppCompatActivity { //创建生成的文件地址 private static final String newPath = "/storage/emulated/0/test.docx"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 0); } Button go = (Button) findViewById(R.id.go); go.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { initData(); } }); } private void initData() { Map<String, Object> map = new HashMap<String, Object>(); map.put("$TITLE$", "标题");//$TITLE$只是个标识符,你也可以用${},[]等等 map.put("$TXT1$", "表格第一行"); map.put("$CONTENT1$", "第一行内容"); map.put("$TXT2$", "表格第二行"); map.put("$CONTENT2$", "第二行内容"); map.put("$CONTENT3$", "页脚中的内容"); map.put("$TXT4$", "页脚表格第一行"); map.put("$CONTENT4$", "页脚第一行内容"); map.put("$TXT5$", "页脚表格第二行"); map.put("$CONTENT5$", "页脚第二行内容"); try { //读取示例文书 InputStream is = getAssets().open("test.docx"); //自定义的XWPFDocument,解决官方版图片不显示问题 CustomXWPFDocument document = new CustomXWPFDocument(is); //读取段落(一般段落,页眉页脚没办法读取) List<XWPFParagraph> listParagraphs = document.getParagraphs(); processParagraphs(listParagraphs, map); //读取页脚 List<XWPFFooter> footerList = document.getFooterList(); processParagraph(footerList, map); //处理表格 Iterator<XWPFTable> it = document.getTablesIterator(); while (it.hasNext()) {//循环操作表格 XWPFTable table = it.next(); List<XWPFTableRow> rows = table.getRows(); for (XWPFTableRow row : rows) {//取得表格的行 List<XWPFTableCell> cells = row.getTableCells(); for (XWPFTableCell cell : cells) {//取得单元格 if ("$IMG$".equals(cell.getText())) { //直接插入图片会在文档的最底端,所以要插在固定位置,要把图片放在表格里 //所以使用判断单元格,并清除单元格放置图片的方式来实现图片定位 cell.removeParagraph(0); XWPFParagraph pargraph = cell.addParagraph(); document.addPictureData(getAssets().open("1.png"), XWPFDocument.PICTURE_TYPE_PNG); document.createPicture(document.getAllPictures().size() - 1, 120, 120, pargraph); } List<XWPFParagraph> paragraphListTable = cell.getParagraphs(); processParagraphs(paragraphListTable, map); } } } FileOutputStream fopts = new FileOutputStream(newPath); document.write(fopts); if (fopts != null) { fopts.close(); } } catch (Exception e) { e.printStackTrace(); } } //处理页脚中的段落,其实就是用方法读取了下页脚中的内容,然后也会当做一般段落处理 private void processParagraph(List<XWPFFooter> footerList, Map<String, Object> map) { if (footerList != null && footerList.size() > 0) { for (XWPFFooter footer : footerList) { //读取一般段落 List<XWPFParagraph> paragraphs = footer.getParagraphs(); processParagraphs(paragraphs, map); //处理表格 List<XWPFTable> tables = footer.getTables(); for (int i = 0; i < tables.size(); i++) { XWPFTable xwpfTable = tables.get(i); List<XWPFTableRow> rows = xwpfTable.getRows(); for (XWPFTableRow row : rows) {//取得表格的行 List<XWPFTableCell> cells = row.getTableCells(); for (XWPFTableCell cell : cells) {//取得单元格 List<XWPFParagraph> paragraphListTable = cell.getParagraphs(); processParagraphs(paragraphListTable, map); } } } } } } //处理段落 public static void processParagraphs(List<XWPFParagraph> paragraphList, Map<String, Object> param) { if (paragraphList != null && paragraphList.size() > 0) { for (XWPFParagraph paragraph : paragraphList) { List<XWPFRun> runs = paragraph.getRuns(); for (XWPFRun run : runs) { String text = run.getText(0); if (text != null) { boolean isSetText = false; for (Map.Entry<String, Object> entry : param.entrySet()) { String key = entry.getKey(); if (text.indexOf(key) != -1) { isSetText = true; Object value = entry.getValue(); if (value instanceof String) {//文本替换 text = text.replace(key, value.toString()); } } } if (isSetText) { run.setText(text, 0); } } } } } }}
    2.5 效果展示
    本文转载自:https://blog.csdn.net/qq_21972583/article/details/82740281
    2 留言 2019-10-08 13:03:54 奖励16点积分
  • Android Studio使用POI读取及修改Word文档(.doc格式)

    一、前言如果你可爱的项目经理要求安卓端的你来操作word实现各种功能,不要犹豫,直接动之以情晓之以理,因为这本来就是java的poi,安卓虽然源自java,但对于java的很多东西是不支持的,已有的各种jar包也不方便更改,各种报错会搞的你脑阔疼。所以编辑word文档这种事让后台来做要比安卓来做简单的多,但如果实在避免不了,接着,给你代码。
    二、说明本篇不支持word2007版,只支持2003版,也就是只支持.doc格式,不支持.docx格式(想要支持.docx格式请参考“Android Studio使用POI读取及修改Word文档(.docx格式)”)。.doc格式的word文档是不支持图片插入的,因为.doc格式和.docx格式有很大的区别,用到的jar包和方法也不同,如果需要插入图片,请查看下篇对.docx格式word文档的处理。
    三、实现过程3.1 制作文书制作.doc格式的文档,然后导入项目:

    文章展示的文档内容如下:

    3.2 导包用到java poi-3.9中的两个包,完整压缩包点这里下载。然后复制用来编辑.doc文档的两个依赖包,导入libs目录。

    如果提示报错,是因为jar中有重复文件,请看build配置。

    加入外部存储读写权限,以后还需要自行申请,代码中有体现:
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    3.3 实现代码读写过程直接上代码了,一看就懂:
    Button go;//生成文件的所在的地址private static final String newPath = "/storage/emulated/0/hwpfdocument.doc";@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); go = (Button) findViewById(R.id.go); go.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { readWord(); } }); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 0); }}private void readWord() { try { //从assets读取我们的Word模板 InputStream is = getAssets().open("hwpfdocument.doc"); //创建生成的文件路径 File newFile = new File(newPath); //需要修改的字段放入map中 Map<String, String> map = new HashMap<String, String>(); map.put("${TITLE}", "标题"); map.put("${TXT}", "表格第一行"); map.put("${CONTENT}", "内容"); writeDoc(is, newFile, map); } catch (IOException e) { e.printStackTrace(); }}private void writeDoc(InputStream is, File newFile, Map<String, String> map) { try { //使用poi的HWPFDocument方法 HWPFDocument hdt = new HWPFDocument(is); //Range读取word文本内容 Range range = hdt.getOverallRange(); //replaceText替换文本内容 for (Map.Entry<String, String> entry : map.entrySet()) { range.replaceText(entry.getKey(), entry.getValue()); } ByteArrayOutputStream ostream = new ByteArrayOutputStream(); FileOutputStream out = new FileOutputStream(newFile, true); hdt.write(ostream); //输出字节流 out.write(ostream.toByteArray()); out.close(); ostream.close(); Toast.makeText(this, "文书已生成", Toast.LENGTH_SHORT).show(); } catch (IOException e) { e.printStackTrace(); }}
    3.4 效果展示
    附录关于Apache POI - HWPF和XWPF的说明可以参考这里。
    关于HWPF的API可以参考这里。左上角选择org.apache.poi.hwpf,然后左下角选择hwpfdocument即可。
    本文转载自:https://blog.csdn.net/qq_21972583/article/details/82385940
    1 留言 2019-10-08 13:04:01 奖励15点积分
  • 基于Skin++库实现的换肤功能

    背景之前自己经常使用MFC来开发一些界面程序,这些程序大都是自己练手用的。但,也会有极个别是帮别人开发,给别人使用。当你辛苦做出来的作品拿出去给别人用的时候,你总想让自己的作品给人留下深刻印象,无论是从功能,还是用程序界面上。
    对于,我们使用 VC6.0 或者 VS2008、VS2010等以上版本开发出来的界面程序,界面通常都是千篇一律,非常质朴的。所以,自己就像用最简单的方式去修改下界面,因为界面虽说重要,但也并不是说不可或缺的,所以自己不想花费太多的精力在界面上。
    后来,经过搜索查找,还真让我找到一个不错的、方便易用的界面修改方法,即使用 Skin++界面库 来实现。Skin++ 属于第二代的外挂式的界面库,提供了Skin Builder 工具将所有控件的资源全部设计成为一个独立的文件,便于在应用程序外部独立地进行增删改操作,采用Hook与子类化技术来实现应用程序的自动换肤。
    现在,本文就基于 Skin++界面库 实现给 VC 或 VS 开发的界面程序更换皮肤。把实现的过程整理成文档,分享给大家。
    实现过程程序要使用到的 Skin++库 文件包括:SkinPPWTL.h、SkinPPWTL.lib、skinppwtl.dll 以及 48个 .ssk 的皮肤文件。
    首先,我们需要把 Skin++ 库文件加载到程序里,做法如下:

    把 SkinPPWTL.h 头文件以及 SkinPPWTL.lib 库文件拷贝到我们的项目工程目录下面,然后在程序声明头文件并加载库文件:
    #include <afxcmn.h>#include <Windows.h>#include "skin\\SkinPPWTL.h"#pragma comment(lib, "skin\\SkinPPWTL.lib")
    然后,我们编译下程序,看看有没有错误提示。若出现类似这样的错误提示: “… _CRT_SECURE_NO_WARNINGS …”,则在 项目属性 —> C/C++ —> 预处理器 —> 预处理器定义 中添加 “__CRT_SECURE_NO_WARNINGS” 这个预定义即可。

    经过上面两步操作,就可以正确地把 Skin++ 库文件加载到程序中了。
    接下来,我们就直接调用 skinppLoadSkin 函数加载 .ssk 格式的皮肤库就可以了。但是要注意调用 skinppLoadSkin 函数的地方,一定要在界面实现出来之前的初始化操作里就开始调用,不能再界面显示出来后调用,否则会出问题。对于 MFC 程序和 Windows应用程序,它们调用 skinppLoadSkin 函数加载界面库文件的地方可以是:

    对于 MFC 程序,可以是在 CxxxAPP::InitInstance() 初始化函数的开头调用,也可以是在 CxxxDlg::OnInitDialog() 主窗口类初始化函数里调用。
    对于 Windows 应用程序,可以是在 WinMain 函数的开头调用,也可以是在窗口消息处理过程函数中的 WM_INITDIALOG 消息中加载。

    调用 skinppLoadSkin 函数加载界面库文件的代码如下所示:
    // 加载皮肤::skinppLoadSkin("skins\\XP-Home.ssk");
    要更换皮肤,只需要更改上面加载的 .ssk 库文件就好,本文有 48 个 .ssk 文件,所以,可以实现 48 种换肤。
    最后,我们编译链接生成可执行文件,在运行程序之前,只需要按照上面库文件 .ssk 的路径放置库文件,同时还需要把 skinppwtl.dll 动态链接库放在和可执行程序同一目录下,就可以正常运行程序了。
    程序测试按照上面的方法,我们分别创建了一个 MFC 程序和 Windows应用程序 来进行测试,程序成功更换皮肤。


    总结对于 Skin++界面库使用比较简单,主要在窗口初始化的时候,就对界面库初始化,并加载界面库就好,剩下的,不需要我们去理会,我们只需要正常开发我们的程序功能就好。
    3 留言 2018-12-20 12:10:51 奖励10点积分
  • 基于python构建搜索引擎系列——(四)检索模型 精华

    构建好倒排索引之后,就可以开始检索了。
    检索模型有很多,比如向量空间模型、概率模型、语言模型等。其中最有名的、检索效果最好的是基于概率的BM25模型。
    给定一个查询Q和一篇文档d,d对Q的BM25得分公式为:

    公式中变量含义如下:

    qtf:查询中的词频
    tf:文档中的词频
    ld:文档长度
    avg_l:平均文档长度
    N:文档数量
    df:文档频率
    b,k1,k3:可调参数

    这个公式看起来很复杂,我们把它分解一下,其实很容易理解。第一个公式是外部公式,一个查询Q可能包含多个词项,比如“苹果手机”就包含“苹果”和“手机”两个词项,我们需要分别计算“苹果”和“手机”对某个文档d的贡献分数w(t,d),然后将他们加起来就是整个文档d相对于查询Q的得分。
    第二个公式就是计算某个词项t在文档d中的得分,它包括三个部分。第一个部分是词项t在查询Q中的得分,比如查询“中国人说中国话”中“中国”出现了两次,此时qtf=2,说明这个查询希望找到的文档和“中国”更相关,“中国”的权重应该更大,但是通常情况下,查询Q都很短,而且不太可能包含相同的词项,所以这个因子是一个常数,我们在实现的时候可以忽略。
    第二部分类似于TFIDF模型中的TF项。也就是说某个词项t在文档d中出现次数越多,则t越重要,但是文档长度越长,tf也倾向于变大,所以使用文档长度除以平均长度ld/avg_l起到某种归一化的效果,k1和b是可调参数。
    第三部分类似于TFIDF模型中的IDF项。也就是说虽然“的”、“地”、“得”等停用词在某文档d中出现的次数很多,但是他们在很多文档中都出现过,所以这些词对d的贡献分并不高,接近于0;反而那些很稀有的词如”糖尿病“能够很好的区分不同文档,这些词对文档的贡献分应该较高。
    所以根据BM25公式,我们可以很快计算出不同文档t对查询Q的得分情况,然后按得分高低排序给出结果。
    下面是给定一个查询句子sentence,根据BM25公式给出文档排名的函数:
    def result_by_BM25(self, sentence): seg_list = jieba.lcut(sentence, cut_all=False) n, cleaned_dict = self.clean_list(seg_list) BM25_scores = {} for term in cleaned_dict.keys(): r = self.fetch_from_db(term) if r is None: continue df = r[1] w = math.log2((self.N - df + 0.5) / (df + 0.5)) docs = r[2].split('\n') for doc in docs: docid, date_time, tf, ld = doc.split('\t') docid = int(docid) tf = int(tf) ld = int(ld) s = (self.K1 * tf * w) / (tf + self.K1 * (1 - self.B + self.B * ld / self.AVG_L)) if docid in BM25_scores: BM25_scores[docid] = BM25_scores[docid] + s else: BM25_scores[docid] = s BM25_scores = sorted(BM25_scores.items(), key = operator.itemgetter(1)) BM25_scores.reverse() if len(BM25_scores) == 0: return 0, [] else: return 1, BM25_scores
    首先将句子分词得到所有查询词项,然后从数据库中取出词项对应的倒排记录表,对记录表中的所有文档,计算其BM25得分,最后按得分高低排序作为查询结果。
    类似的,我们还可以对所有文档按时间先后顺序排序,越新鲜的新闻排名越高;还可以按新闻的热度排序,越热门的新闻排名越高。
    关于热度公式,我们认为一方面要兼顾相关度,另一方面也要考虑时间因素,所以是BM25打分和时间打分的一个综合。
    比较有名的热度公式有两个,一个是Hacker News的,另一个是Reddit的,他们的公式分别为:


    可以看出,他们都是将新闻/评论的一个原始得分和时间组合起来,只是一个用除法,一个用加法。所以我们也依葫芦画瓢,”自创“了一个简单的热度公式:

    用BM25得分加上新闻时间和当前时间的差值的倒数,k1k1和k2k2也是可调参数。
    按时间排序和按热度排序的函数和按BM25打分排序的函数类似,这里就不贴出来了,详细情况可以看我的项目News_IR_Demo。
    至此,搜索引擎的搜索功能已经实现了,你可以试着修改./web/search_engine.py的第167行的关键词,看看搜索结果是否和你预想的排序是一样的。不过由于我们的数据量只有1000个新闻,并不能涵盖所有关键词,更多的测试可以留给大家线下完成。
    本文转载自:http://bitjoy.net/2016/01/07/introduction-to-building-a-search-engine-4
    2 留言 2019-06-01 15:30:22 奖励14点积分
  • windows下静态使用QxOrm框架并使用泛型编程 (二)

    这篇开始讲实际编程并且抽象化,让代码书写更少。
    为了模块划分我在Demo文件夹下新增了一个SQLModule文件夹,在此文件夹下又新增QxHandler,QxMapped,QxObject三个文件夹,QxObject是用来存储数据对象类的一个文件夹,QxMapped是存储键值映射的文件夹,QxHandler是操作数据库的实际句柄类文件夹。
    QxObject类
    User.h
    #ifndef USER_H#define USER_H#include "common.h"#include <QxOrm.h>#include <QJsonObject>class User{ QX_REGISTER_FRIEND_CLASS(User) //用于将类注册到QxOrm的宏定义public: User(); long getId(); QString getName(); int getAge();public: QJsonObject toJsonObject(); bool analyzeJson(QByteArray &json); void analyzeJson(QJsonObject &json);private: long m_lId QString m_sName; int m_nAge;};//QX_REGISTER_PRIMARY_KEY(User, int) //主键不是整数类型的时候使用QX_REGISTER_HPP_IMPORT_DLL(User, qx::trait::no_base_class_defined, DATABASE_VERSION) //用于将类注册到QxOrm的宏定义 第一个参数为类名 第二个为默认值 第三个为数据库版本#endif // USER_HUser.cpp
    #include "User.h"QX_REGISTER_CPP_IMPORT_DLL(User) //用于将类注册到QxOrm的宏定义namespace qx //用于将类注册到QxOrm的方法 { template <> void register_class(QxClass<User> & t) { qx::IxDataMember * pData = NULL; Q_UNUSED(pata); qx::IxSqlRelation * pRelation = NULL; Q_UNUSED(pRelation); qx::IxFunction * pFct = NULL; Q_UNUSED(pFct) qx::IxValidator * pValidator = NULL; Q_UNUSED(pValidator); // Register pData =t.id(& User::m_lId, "id",DATABASE_VERSION); pData =t.data(& User::m_nAge, "name",DATABASE_VERSION); pData =t.data(& User::m_nAge, "age",DATABASE_VERSION); qx::QxValidatorX<User> * pAllValidator = t.getAllValidator(); Q_UNUSED(pAllValidator); }}User::User() :m_lId(0) ,m_sName("") ,m_nAge(0){}long User::getId(){ return m_lId;}QString User::getName(){ return m_sName;}int User::getAge(){ return m_nAge}JsonObject User::toJsonObject(){ QJsonObject subObject; subObject.insert("Name",m_sName); subObject.insert("Age",m_nAge); return subObject;}bool User::analyzeJson(QByteArray &json){ bool success=false; QJsonParseError error; QJsonDocument document=QJsonDocument::fromJson(json,&error); if (!document.isNull() && (error.error == QJsonParseError::NoError)) { QJsonObject rootObject =documnt.object(); this->analyzeJson(rootObject); success=true; } else { success=false; qDebug()<<error.error; qDebug("LockoutFun analyze falied"); } return success;}void User::analyzeJson(QJsonObject &json){ QJsonObject rootObject = json.value("User").toObject(); m_sName=rootObject.value(QString("Name")).toString(); m_nAge=rootObject.value(QString("Age")).toInt();}QxMapped
    IMapped 映射抽象类
    #ifndef IMAPPED_H#define IMAPPED_H/*** @author tianmin* @brief The IMapped class 抽象出来的映射类* @date 2019-07-22*/#include <QStringList>#include <QSting>#include <QMapclass IMapped{public //字符map初始化 virtual void initMapped() = 0; //获取QString列表 virtual QStringList getListMapped(int en) =0; //获取QString 字段 virtual QString getMapped(int en) = 0; virtual ~IMapped(){}};#endif // IMAPPED_HUserMapped 际使用的类
    UserMapped .h
    #ifndef USERMAPPED_H#define USERMAPPED_H#include "IMapped.h"namespace USER {enum USER_MAPPED{ EN_ID = 0x00000001, EN_NAME = 0x00000002, EN_AGE = 0x00000004, EN_ALL = 0xFFFFFFFF};}class UserMapped:public IMapped{public: UserMapped(); virtual ~UserMapped();public: virtual void initMapped() virtual QStringList getListMapped(int en); virtual QString getMapped(int en);private: QMap<int,QString> m_map;};#endif // USERMAPPED_HUserMapped.cpp
    #include "UserMapped.h"UserMapped::UserMapped(){}UserMapped::~UserMapped(){}void UserMapped::initMapped(){ m_map.clear(); m_map.insert(USER::EN_ID, "id"); m_map.insert(USER::EN_NAME ,"name"); m_map.insert(USER::EN_AGE ,"age");}QStringList UserMapped::getListMapped(int en){ QStringList temp; QString str; QMap<int,QString>::iterator i; or (i = m_map.begin(); i != m_map.end(); ++i) { if(en&(i.key())) { str=i.value(); temp.append(str); str.clear(); } } return temp;}QString UserMapped::getMapped(int en){ QString str QMap<int,QString>::iterator i; for (i = m_map.begin(); i != m_map.end(); ++i) { if(en==i.key()) { str =i.value(); return str; } } return QString();}QxHandler
    IHandler 抽象化的类模板
    这里只抽象了部分方法 还有save 和事务以及关系的方法未抽象完成
    #ifndef IHANDLER_H#define IHANDLER_H#include <QxOrm.h>#include <QMutexLocker>#include <QMutex>#include <QtSql/QSqlDatabase>#include <QtSql/QSqlError>/*** @brief The ISqlInterface class 数据库操作的抽象方法类 并且模板化减少代码的繁杂*/class ISqlInterface{public: ISqlInterface(){} virtual ~ISqlInterface(){} //数据库连接初始化protected: virtual void initSqlconnect()=0; //建表 virtual bool createTable()=0; //断开连接 virtual void disconnect()=0;};template<class T,class T2,class T3>class IHandler{public: IHandler(){} virtual ~IHandler(){} Virtual bool createTable(QSqlDatabase &m_SqlDatabase) { QSqlError error= qx::dao::create_table<T3>(&m_SqlDatabase); return !error.isValid(); } virtual bool insert(T &t,QMutex &m_Mutex,QSqlDatabase &m_SqlDatabase) { QMutexLocker locker(&m_Mutex); if(!m_SqlDatabase.isOpen()) return false; if(qx::dao::exist(t,&m_SqlDatabase).getValue()!=false) return false; QSqlError error= qx::dao::insert(t,&m_SqlDatabase); return !error.isValid(); } virtual bool deleteObject(T &t,QMutex &m_Mutex,QSqlDatabase &m_SqlDatabase,bool isDestroy) { QMutexLocker locker(&m_Mutex); if(!m_SqlDatabase.isOpen()) return false; QSqlError error; if(qx::dao::exist(t,&m_SqlDatabase).getValue()==false) return false; if(isDestroy==false) { error= qx::dao::delete_by_id(t,&m_SqlDatabase); } else { error= qx::dao::destroy_by_id(t,&m_SqlDatabase); } return !error.isValid(); } virtual bool update(T &t,QMutex &m_Mutex,QSqlDatabase &m_SqlDatabase,QStringList &list) { QMutexLocker locker(&m_Mutex); if(!m_SqlDatabas.isOpen()) return false; if(qx::dao::exist(t,&m_SqlDatabase).getValue()==false) return false; QSqlError error= qx::dao::update(t,&m_SqlDatabase,list); return !error.isValid(); } virtual bool select(T &t,QMutex &m_Mutex,QSqlDatabase &m_SqlDatabase,QStringList &list) QMutexLocker locker(&m_Mutex); if(!m_SqlDatabase.isOpen()) return false; QSqlError error; if(qx::dao::exist(t,&m_SqlDatabase).getValue()==false) return false; error= qx::dao::fetch_by_id(t,&m_SqlDatabase,list); return !error.isValid(); } virtual bool selectByQuery(T2 &t,QMutex &m_Mutex,QSqlDatabase &m_SqlDatabase,qx::QxSqlQuery &query,QStringList &list) { QMutexLocker locker(&m_Mutx); if(!m_SqlDatabase.isOpen()) return false; QSqlError error=qx::dao::fetch_by_query(query,t,&m_SqlDatabase,list); return !error.isValid(); }};#endif // IHANDLER_HUserHandler 实际操作句柄类
    UserHandler.h
    #ifndef USERHANDLER_H#define USERHANDLER_H#include <QxOrm.h>#include <QString>#include <QMutexLocker>#include <QMutex>#include <QtSql/QSqlDatabase>#include <QtSql/QSqlError>#include "IHandler.h"#include "SQLModule/QxMapped/UserMapped.h"#include "SQLModule/QxObject/User.h"namespace USER{const QString DATABASE_TYPE="QSQLITE";const QString CONNECT_NAME="USER_CONNECTED";const QString DATABASENAME="C:/Users/we/Desktop/workTools/demo/qxorm.db";const QString HOSTNAME="localhost";const QString USERNAME="root";const QString PASSWORD="";}using namespace USER;class User; //OBJECT 类typedef QSharedPointer<User> Shared_User; //User类智能指针typedef QList<Shared_User> List_User; //User类数组typedef qx::QxCollection<int,Shared_User> Collection_User; //User容器class UserHandler:public IHandler<Shared_User,Collection_User,User> ,public ISqlInterface{public: UserHandler(); virtual ~UserHandler(); /** * @brief insert 插入数据至数据库 * @param t 插入的单条数据 不需要指定ID值 自增 * @return 0失败 1成功 */ bool insert(Shared_User &t); /** * @brief deleteObject 从数据库中删除指定数据 * @param t 删除的单条数据 需要指定ID值 * @param isDestroy 是否软删除 * @return 0失败 1成功 */ bool deleteObject(Shared_User &t,bool isDestroy=false); /** * @brief update 根据ID值更新数据 * @param t 数据 * @param en 更新字段的映射值 * @return 0失败 1成功 */ bool update(Shared_User &t,int en=EN_ALL); /** * @brief select 根据ID值查询数据 * @param t 数据 * @param en 映射值 * @return 0失败 1成功 */ bool select(Shared_User &t,int en=EN_ALL); /** * @brief selectByQuery 根据搜寻条件查找 * @param t 数据集合 * @param query 搜寻语句 * @param en 数据库列映射值 * @return 0失败 1成功 */ bool selectByQuery(Collection_User &t,qx::QxSqlQuery &query,int en=EN_ALL);protected: virtual void initSqlconnect(); virtual bool createTable(); virtual void disconnect();private: UserMapped m_Mapped; QMutex m_Mutex; QSqlDatabase m_SqlDatabase;};#endif // USERHANDLER_HUserHandler.cpp
    #include "UserHandler.h"UserHandler::UserHandler(){ initSqlconnect(); createTable();}UserHandler::~UserHandler(){ disconnect();}void UserHandler::initSqlconnect(){ QMutexLocker locker(&m_Mutex); if(QSqlDatabase::contains(CONNECT_NAME)) m_SqlDatabase = QSqlDatabase::database(CONNECT_NAME); else m_SqlDatabase= QSqlDatabase::addDatabase(DATABASE_TYPE,CONNECT_NAME); m_SqlDatabase.setDatabaseName(DATABASENAME); m_SqlDatabase.setHostName(HOSTNAME); m_SqlDatabase.setUserName(USERNAME); m_SqlDatabase.setPassword(PASSWORD); m_SqlDatabase.open();}bool UserHandler::createTable(){ return IHandler<Shared_User,Collection_User,User>::createTable(m_SqlDatabase);}void UserHandler::disconnect(){ QMutexLocker locker(&m_Mutex); if(m_SqlDatabase.isOpen()) m_SqlDatabase.close(); QSqlDatabase::removeDatabase(CONNECT_NAME);}bool UserHandler::insert(Shared_User &t){ return IHandler<Shared_User,Collection_User,User>::insert(t,m_Mutex,m_SqlDatabase);}bool UserHandler::deleteObject(Shared_User &t,bool isDestroy){ return IHandler<Shared_User,Collection_User,User>::deleteObject(t,m_Mutex,m_SqlDatabase,isDestroy);}bool UserHandler::update(Shared_User &t, int en){ QStringList list= m_Mapped.getListMapped(en); return IHandler<Shared_User,Collection_User,User>::update(t,m_Mutex,m_SqlDatabase,list);}bool UserHandler::select(Shared_User &t, int en){ QStringList list= m_Mapped.getListMapped(en); return IHandler<Shared_User,Collection_User,User>::select(t,m_Mutex,m_SqlDatabase,list);}bool UserHandler::selectByQuery(Collection_User &t,qx::QxSqlQuery &query,int en){ QStringList list= m_Mapped.getListMapped(en); return IHandler<Shared_User,Collection_User,User>::selectByQuery(t,m_Mutex,m_SqlDatabase,query,list);}
    1 留言 2019-07-23 22:47:54 奖励6点积分
  • 【Cocos Creator 实战教程(1)】——人机对战五子棋(节点事件相关) 精华

    一、涉及知识点
    场景切换按钮事件监听节点事件监听节点数组循环中闭包的应用动态更换sprite图片定时器预制资源
    二、步骤2.1 准备工作首先,我们要新建一个空白工程,并在资源管理器中新建几个文件夹

    在这些文件夹中,我们用来存放不同的资源,其中

    Scene用来存放场景,我们可以把场景看作一个关卡,当关卡切换时,场景就切换了
    Script用来存放脚本文件
    Texture用来存放显示的资源,例如音频,图片
    Prefab用来存放预制资源,接下来我会详细的介绍

    接下来,我们在Scene文件夹中新建一个场景(右击文件夹->新建->Scene),命名为Menu,接着导入背景图片(直接拖拽即可)。最后调整图片大小使图片铺满背景,效果如图。

    2.2 按钮监听与场景切换接下来我们来学习此次实战的第一个难点,按钮监听与场景切换。
    首先,创建一个Button节点,并删除label,放在人机博弈的按钮上,并在属性上调成透明样式。

    接下来,新建一个Game场景,并添加一个棋盘节点,并把锚点设为(0,0)。

    这里,讲一下锚点的作用。
    anchor point 究竟是怎么回事? 之所以造成不容易理解的是因为我们平时看待一个图片是以图片的中心点这一个维度来决定图片的位置的。而在cocos2d中决定一个图片的位置是由两个维度一个是 position 另外一个是anchor point。只要我们搞清楚他们的关系,自然就迎刃而解。默认情况下,anchor point在图片的中心位置(0.5, 0.5),取值在0到1之间的好处就是,锚点不会和具体物体的大小耦合,也即不用关注物件大小,而应取其对应比率,如果把锚点改成(0,0),则进行放置位置时,以图片左下角作为起始点。也就是说,把position设置成(x,y)时,画到屏幕上需要知道:到底图片上的哪个点放在屏幕的(x,y)上,而anchor point就是这个放置的点,anchor point是可以超过图片边界的,比如下例中的(-1,-1),表示从超出图片左下角一个宽和一个高的地方放置到屏幕的(0,0)位置(向右上偏移10个点才开始到图片的左下角,可以认为向右上偏移了10个点的空白区域)
    他们的关系是这样的(假设actualPosition.x,actualPosition.y是真实图片位置的中点):actualPosition.x = position.x + width*(0.5 - anchor_point.x);acturalPosition.y = position.y + height*(0.5 - anchor_point.y)actualPosition 是sprite实际上在屏幕显示的位置, poistion是 程序设置的, achor_point也是程序设置的。
    然后,我们需要新建一个脚本,Menu.js,并添加开始游戏方法。
    cc.Class({ extends: cc.Component, startGame:function(){ cc.director.loadScene('Game');//这里便是运用导演类进行场景切换的代码 }});
    这里提示以下,编辑脚本是需要下载插件的,我选择了VScode,还是很好用的。
    最后我们将其添加为Menu场景的Canvas的组件(添加组件->脚本组件->menu),并在BtnP2C节点上添加按钮监听响应。


    这样,按钮监听就完成了。现在我们在Menu场景里点击一下人机按钮就会跳转到游戏场景了。
    2.3 预制资源预制资源一般是在场景里面创建独立的子界面或子窗口,即预制资源是存放在资源中,并不是节点中,例如本节课中的棋子。
    现在我们就来学习一下如何制作预制资源。

    再将black节点改名为Chess拖入下面Prefab文件夹使其成为预制资源。
    这其中,SpriteFrame 是核心渲染组件 Sprite 所使用的资源,设置或替换 Sprite 组件中的 spriteFrame 属性,就可以切换显示的图像,将其去掉防止图片被预加载,即棋子只是一个有大小的节点不显示图片也就没有颜色。
    2.4 结束场景直接在Game场景上制作结束场景。

    2.5 游戏脚本制作人机对战算法参考了这里https://blog.csdn.net/onezeros/article/details/5542379
    具体步骤便是初始化棋盘上225个棋子节点,并为每个节点添加事件,点击后动态显示棋子图片。
    代码如下:
    cc.Class({ extends: cc.Component, properties: { overSprite:{ default:null, type:cc.Sprite, }, overLabel:{ default:null, type:cc.Label }, chessPrefab:{//棋子的预制资源 default:null, type:cc.Prefab }, chessList:{//棋子节点的集合,用一维数组表示二维位置 default: [], type: [cc.node] }, whiteSpriteFrame:{//白棋的图片 default:null, type:cc.SpriteFrame }, blackSpriteFrame:{//黑棋的图片 default:null, type:cc.SpriteFrame }, touchChess:{//每一回合落下的棋子 default:null, type:cc.Node, visible:false//属性窗口不显示 }, gameState:'white', fiveGroup:[],//五元组 fiveGroupScore:[]//五元组分数 }, //重新开始 startGame(){ cc.director.loadScene("Game"); }, //返回菜单 toMenu(){ cc.director.loadScene("Menu"); }, onLoad: function () { this.overSprite.node.x = 10000;//让结束画面位于屏幕外 var self = this; //初始化棋盘上225个棋子节点,并为每个节点添加事件 for(var y = 0;y<15;y++){ for(var x = 0;x < 15;x++){ var newNode = cc.instantiate(this.chessPrefab);//复制Chess预制资源 this.node.addChild(newNode); newNode.setPosition(cc.p(x*40+20,y*40+20));//根据棋盘和棋子大小计算使每个棋子节点位于指定位置 newNode.tag = y*15+x;//根据每个节点的tag就可以算出其二维坐标 newNode.on(cc.Node.EventType.TOUCH_END,function(event){ self.touchChess = this; if(self.gameState === 'black' && this.getComponent(cc.Sprite).spriteFrame === null){ this.getComponent(cc.Sprite).spriteFrame = self.blackSpriteFrame;//下子后添加棋子图片使棋子显示 self.judgeOver(); if(self.gameState == 'white'){ self.scheduleOnce(function(){self.ai()},1);//延迟一秒电脑下棋 } } }); this.chessList.push(newNode); } } //开局白棋(电脑)在棋盘中央下一子 this.chessList[112].getComponent(cc.Sprite).spriteFrame = this.whiteSpriteFrame; this.gameState = 'black'; //添加五元数组 //横向 for(var y=0;y<15;y++){ for(var x=0;x<11;x++){ this.fiveGroup.push([y*15+x,y*15+x+1,y*15+x+2,y*15+x+3,y*15+x+4]); } } //纵向 for(var x=0;x<15;x++){ for(var y=0;y<11;y++){ this.fiveGroup.push([y*15+x,(y+1)*15+x,(y+2)*15+x,(y+3)*15+x,(y+4)*15+x]); } } //右上斜向 for(var b=-10;b<=10;b++){ for(var x=0;x<11;x++){ if(b+x<0||b+x>10){ continue; }else{ this.fiveGroup.push([(b+x)*15+x,(b+x+1)*15+x+1,(b+x+2)*15+x+2,(b+x+3)*15+x+3,(b+x+4)*15+x+4]); } } } //右下斜向 for(var b=4;b<=24;b++){ for(var y=0;y<11;y++){ if(b-y<4||b-y>14){ continue; }else{ this.fiveGroup.push([y*15+b-y,(y+1)*15+b-y-1,(y+2)*15+b-y-2,(y+3)*15+b-y-3,(y+4)*15+b-y-4]); } } } }, //电脑下棋逻辑 ai:function(){ //评分 for(var i=0;i<this.fiveGroup.length;i++){ var b=0;//五元组里黑棋的个数 var w=0;//五元组里白棋的个数 for(var j=0;j<5;j++){ this.getComponent(cc.Sprite).spriteFrame if(this.chessList[this.fiveGroup[i][j]].getComponent(cc.Sprite).spriteFrame == this.blackSpriteFrame){ b++; }else if(this.chessList[this.fiveGroup[i][j]].getComponent(cc.Sprite).spriteFrame == this.whiteSpriteFrame){ w++; } } if(b+w==0){ this.fiveGroupScore[i] = 7; }else if(b>0&&w>0){ this.fiveGroupScore[i] = 0; }else if(b==0&&w==1){ this.fiveGroupScore[i] = 35; }else if(b==0&&w==2){ this.fiveGroupScore[i] = 800; }else if(b==0&&w==3){ this.fiveGroupScore[i] = 15000; }else if(b==0&&w==4){ this.fiveGroupScore[i] = 800000; }else if(w==0&&b==1){ this.fiveGroupScore[i] = 15; }else if(w==0&&b==2){ this.fiveGroupScore[i] = 400; }else if(w==0&&b==3){ this.fiveGroupScore[i] = 1800; }else if(w==0&&b==4){ this.fiveGroupScore[i] = 100000; } } //找最高分的五元组 var hScore=0; var mPosition=0; for(var i=0;i<this.fiveGroupScore.length;i++){ if(this.fiveGroupScore[i]>hScore){ hScore = this.fiveGroupScore[i]; mPosition = (function(x){//js闭包 return x; })(i); } } //在最高分的五元组里找到最优下子位置 var flag1 = false;//无子 var flag2 = false;//有子 var nPosition = 0; for(var i=0;i<5;i++){ if(!flag1&&this.chessList[this.fiveGroup[mPosition][i]].getComponent(cc.Sprite).spriteFrame == null){ nPosition = (function(x){return x})(i); } if(!flag2&&this.chessList[this.fiveGroup[mPosition][i]].getComponent(cc.Sprite).spriteFrame != null){ flag1 = true; flag2 = true; } if(flag2&&this.chessList[this.fiveGroup[mPosition][i]].getComponent(cc.Sprite).spriteFrame == null){ nPosition = (function(x){return x})(i); break; } } //在最最优位置下子 this.chessList[this.fiveGroup[mPosition][nPosition]].getComponent(cc.Sprite).spriteFrame = this.whiteSpriteFrame; this.touchChess = this.chessList[this.fiveGroup[mPosition][nPosition]]; this.judgeOver(); }, judgeOver:function(){ var x0 = this.touchChess.tag % 15; var y0 = parseInt(this.touchChess.tag / 15); //判断横向 var fiveCount = 0; for(var x = 0;x < 15;x++){ if((this.chessList[y0*15+x].getComponent(cc.Sprite)).spriteFrame === this.touchChess.getComponent(cc.Sprite).spriteFrame){ fiveCount++; if(fiveCount==5){ if(this.gameState === 'black'){ this.overLabel.string = "你赢了"; this.overSprite.node.x = 0; }else{ this.overLabel.string = "你输了"; this.overSprite.node.x = 0; } this.gameState = 'over'; return; } }else{ fiveCount=0; } } //判断纵向 fiveCount = 0; for(var y = 0;y < 15;y++){ if((this.chessList[y*15+x0].getComponent(cc.Sprite)).spriteFrame === this.touchChess.getComponent(cc.Sprite).spriteFrame){ fiveCount++; if(fiveCount==5){ if(this.gameState === 'black'){ this.overLabel.string = "你赢了"; this.overSprite.node.x = 0; }else{ this.overLabel.string = "你输了"; this.overSprite.node.x = 0; } this.gameState = 'over'; return; } }else{ fiveCount=0; } } //判断右上斜向 var f = y0 - x0; fiveCount = 0; for(var x = 0;x < 15;x++){ if(f+x < 0 || f+x > 14){ continue; } if((this.chessList[(f+x)*15+x].getComponent(cc.Sprite)).spriteFrame === this.touchChess.getComponent(cc.Sprite).spriteFrame){ fiveCount++; if(fiveCount==5){ if(this.gameState === 'black'){ this.overLabel.string = "你赢了"; this.overSprite.node.x = 0; }else{ this.overLabel.string = "你输了"; this.overSprite.node.x = 0; } this.gameState = 'over'; return; } }else{ fiveCount=0; } } //判断右下斜向 f = y0 + x0; fiveCount = 0; for(var x = 0;x < 15;x++){ if(f-x < 0 || f-x > 14){ continue; } if((this.chessList[(f-x)*15+x].getComponent(cc.Sprite)).spriteFrame === this.touchChess.getComponent(cc.Sprite).spriteFrame){ fiveCount++; if(fiveCount==5){ if(this.gameState === 'black'){ this.overLabel.string = "你赢了"; this.overSprite.node.x = 0; }else{ this.overLabel.string = "你输了"; this.overSprite.node.x = 0; } this.gameState = 'over'; return; } }else{ fiveCount=0; } } //没有输赢交换下子顺序 if(this.gameState === 'black'){ this.gameState = 'white'; }else{ this.gameState = 'black'; } }});
    这里便用到了节点监听,节点数组,定时器和动态更换sprite图片。
    新建Game脚本添加到ChessBoard节点下

    3 注意课程到这里就结束了,本课程部分资源来源于网络。
    第一次写技术分享,如果大家有什么好的建议和问题请在下方留言区留言。
    7 留言 2018-12-20 12:17:01 奖励20点积分
  • 深度学习 20、CNN

    前文链接:https://write-bug.com/article/2575.html
    卷积神经网络CNN上节我们介绍了DNN的基本网络结构和演示,其网络结构为:

    DNN BP神经网络实行全连接特性,参数权值过多,需求大量样本,计算困难,所以CNN利用局部连接、权值共享来实现减少权值的尝试,即局部感知野和权值共享。擅长图像分类问题,识别二维图形,现在也可以用于文本处理。

    CNN的隐藏层分为卷积层conv、和池化层pool,一般CNN结构(LeNet)依次为:

    INPUT->[[CONV -> RELU]N -> POOL?]M -> [FC -> RELU]*K->FC经典LeNet-5结构:最早用于数字识别的CNN


    卷积层Convolitions:
    用卷积层实现对图片局部的特征提取,窗口模式卷积核形式提取特征,映射到高维平面。
    两个关键性操作:

    局部关联:每个神经元看为一个滤波器filter
    窗口滑动:filter对局部数据计算

    局部感受野:
    1000 * 1000 图像 1000 * 1000神经元全连接: 1000 * 1000 * 1000 * 1000 = 10^12 个参数
    局部感受野:每个神经元只需要知道10 * 10区域,训练参数降到100M

    权值共享:
    所有神经元用同一套权重值,100M降到100,这样得到一个feature map。100种权重值100个map,一共需要训练100*100 = 10k参数

    卷积的计算:

    窗口滑动,我们可以想象一个窗口(卷积核feature_map),设置固定的feature_size,里面是固定的权值,这个窗口在同一张图片上从左到右从上到下游走,每次固定步长,从而得到更小size的矩阵。

    这里有两个神经元的卷积层,步长stride设置为2,边缘灰色部分为填充值zero-padding,防止窗口移到边缘时不够滑动遍历所有像素,而这里的神经元个数就是卷积层深度depth。
    在卷积层中每个神经元连接数据窗的权重是固定的,每个神经元只关注一个特性。神经元就是图像处理中的滤波器,比如边缘检测专用的Sobel滤波器,即卷积层的每个滤波器都会有自己所关注一个图像特征,比如垂直边缘,水平边缘,颜色,纹理等等,这些所有神经元加起来就好比就是整张图像的特征提取器集合。

    需要估算的权重个数减少: AlexNet 1亿 => 3.5w
    一组固定的权重和不同窗口内数据做内积: 卷积卷积理解:后面输出结果的前提是依赖前面相应权值积累,比如连续吃三天药身体才好,那么这三天的相关性是不同的权值

    激活函数把卷积层输出结果做非线性映射。CNN采用的激励函数一般为ReLU(The Rectified Linear Unit/修正线性单元),它的特点是收敛快,求梯度简单,但较脆弱,图像如下。

    激励层的实践经验:

    不要用sigmoid!不要用sigmoid!不要用sigmoid!首先试RELU,因为快,但要小心点如果2失效,请用Leaky ReLU或者Maxout某些情况下tanh倒是有不错的结果,但是很少
    池化层pooling:对特征进一步缩放
    池化层夹在连续的卷积层中间,用于压缩数据和参数的量,减小过拟合,压缩图像。
    特点:

    特征不变性:图像缩小还可以认出大概
    特征降维:去除冗余信息,防止过拟合


    池化层用的方法有Max pooling 和 average pooling,而实际用的较多的是Max pooling。
    这里就说一下Max pooling,其实思想非常简单。

    对于每个2*2的窗口选出最大的数作为输出矩阵的相应元素的值,比如输入矩阵第一个2*2窗口中最大的数是6,那么输出矩阵的第一个元素就是6,如此类推。
    平移不变性

    旋转不变性

    缩放不变性

    全连接层FC
    和BP神经网络一样,两层之间互相全连接,即把压缩后的维度打平为一组向量,再通过dnn的方法进行分类处理。
    Droupout
    正则化方法,CNN中解决过拟合问题,同时通用与其他网络。

    随机在全连接层,剪掉一些神经元,防止过拟合。
    卷积网络在本质上是一种输入到输出的映射,它能够学习大量的输入与输出之间的映射关系,而不需要任何输入和输出之间的精确的数学表达式,只要用已知的模式对卷积网络加以训练,网络就具有输入输出对之间的映射能力。
    CNN一个非常重要的特点就是头重脚轻(越往输入权值越小,越往输出权值越多),呈现出一个倒三角的形态,这就很好地避免了BP神经网络中反向传播的时候梯度损失得太快。
    卷积神经网络CNN主要用来识别位移、缩放及其他形式扭曲不变性的二维图形。由于CNN的特征检测层通过训练数据进行学习,所以在使用CNN时,避免了显式的特征抽取,而隐式地从训练数据中进行学习;再者由于同一特征映射面上的神经元权值相同,所以网络可以并行学习,这也是卷积网络相对于神经元彼此相连网络的一大优势。卷积神经网络以其局部权值共享的特殊结构在语音识别和图像处理方面有着独特的优越性,其布局更接近于实际的生物神经网络,权值共享降低了网络的复杂性,特别是多维输入向量的图像可以直接输入网络这一特点避免了特征提取和分类过程中数据重建的复杂度。
    卷积神经网络之训练算法:

    同一般机器学习算法,先定义Loss function,衡量和实际结果之间差距
    找到最小化损失函数的W和b, CNN中用的算法是SGD(随机梯度下降)

    卷积神经网络之优缺:

    优点

    共享卷积核,对高维数据处理无压力无需手动选取特征,训练好权重,即得特征分类效果好
    缺点

    需要调参,需要大样本量,训练最好要GPU物理含义不明确(也就说,我们并不知道没个卷积层到底提取到的是什么特征,而且神经网络本身就是一种难以解释的“黑箱模型”)

    卷积神经网络之典型CNN:

    LeNet,这是最早用于数字识别的CNN
    AlexNet, 2012 ILSVRC比赛远超第2名的CNN,比LeNet更深,用多层小卷积层叠加替换单大卷积层。
    ZF Net, 2013 ILSVRC比赛冠军
    GoogLeNet, 2014 ILSVRC比赛冠军
    VGGNet, 2014 ILSVRC比赛中的模型,图像识别略差于GoogLeNet,但是在很多图像转化学习问题(比如object detection)上效果奇好pytorch网络结构:

    class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(1, 20, 5, 1) self.conv2 = nn.Conv2d(20, 50, 5, 1) self.fc1 = nn.Linear(4*4*50, 500) self.fc2 = nn.Linear(500, 10) def forward(self, x): print("x size: ", x.size()) x = F.relu(self.conv1(x)) print("conv1 size: ", x.size()) x = F.max_pool2d(x, 2, 2) print("pool size: ", x.size()) x = F.relu(self.conv2(x)) print("conv2 size: ", x.size()) x = F.max_pool2d(x, 2, 2) print("pool size: ", x.size()) x = x.view(-1, 4*4*50) print("view size: ", x.size()) x = F.relu(self.fc1(x)) print("fc1 size: ", x.size()) x = self.fc2(x) print("fc2 size: ", x.size()) sys.exit() return F.log_softmax(x, dim=1)
    1 留言 2019-06-11 14:23:58 奖励13点积分
  • 内核双机调试——追踪并分析内核API函数的调用 精华

    在64位Win10主机上调试32位Win7虚拟机内核,查看Win7内核中的函数调用关系,从而分析API函数实现的具体原理和流程。
    一、环境配置前提,对Win7虚拟机设置了调试的COM端口:\\.\pipe\com_1,波特率设置为115200,那么接下来就可以使用WinDbg来进行调试了。具体步骤如下所示:
    首先,在Win10上“以管理员身份”运行64位的WinDbg,点击File—>Kernel Debug—>COM,波特率设置为115200,端口与上面对应\\.\pipe\com_1,并勾选pipe、Reconnect,点击确定即可。

    如果我们不动它,WinDbg命令窗口将会一直显示“Waiting to reconnect…”的信息!这时,我们需要点击下工具栏上“Break”按钮,让Win7系统断下来,这样我们才可以用WinDbg进行调试,输入控制指令。


    接着,设置符号路径:File—>Symbol File Path—>输入:
    srv*c:\symbols*https://msdl.microsoft.com/download/symbols确定即可,这样WinDbg就会自动根据链接下载符号表到本地上。

    等待一会儿,即可下载完毕,方可输入指令。
    二、使用WingDbg追踪API调用流程接下来,我们以分析内核API函数NtQueryDirectoryFile为例,介绍WinDbg调试软件的使用方法。
    输入指令:uf nt!NtQueryDirectoryFile

    大概可以看出,NtQueryDirectoryFile函数的实现主要是调用了nt!BuildQueryDirectoryIrp函数构造查询文件的IRP以及nt!IopSynchronousServiceTail发送IRP来实现的。为了验证我们的想法,我们继续对这两个函数进行查看。
    输入指令:uf nt!BuildQueryDirectoryIrp

    从它函数实现调用了nt!IoAllocateIrp函数可知,我们的猜想是正确的。
    输入指令:uf nt!IopSynchronousServiceTail

    从它函数实现调用了nt!IofCallDriver函数可知,我们的猜想是正确的。
    nt!IofCallDriver函数定义如下所示:
    NTSTATUS IofCallDriver( PDEVICE_OBJECT DeviceObject, __drv_aliasesMem PIRP Irp);
    该函数的功能就是将IRP发送到指定的设备对象中处理,第1个参数就是处理IRP的设备对象。
    所以,接下来,我们继续用WinDbg分析上述nt!IopSynchronousServiceTail函数将IRP发给了哪个驱动设备进行处理的。
    输入指令:bu nt!NtQueryDirectoryFile
    输入指令:bl;查看所有断点
    输入指令:g;继续往下执行
    下断点,只要系统执行到nt!NtQueryDirectoryFile这个函数就会停下来。

    输入指令:u @eip
    输入指令:r;查看寄存器

    由于是对nt! NtQueryDirectoryFile这个函数下断点,所以现在停下来,指令指针eip指向的就是nt! NtQueryDirectoryFile函数的入口地址。此时,uf @eip就是反汇编nt! NtQueryDirectoryFile函数的内容。
    输入指令:bp 84043fdc
    输入指令:g

    84043fdc就是nt!IopSynchronousServiceTail函数的入口地址,断点会自动在此处断下。我们一步步下断点,逼近最终我们需要下断点的nt!IofCallDriver函数,确保是由nt!NtQueryDirectoryFile函数内容实现调用的nt!IofCallDriver函数。
    输入指令:uf @eip

    输入指令:bp 83e4cd19
    输入指令:g

    83e4cd19是nt!IofCallDriver函数的入口地址,WinDbg断点断下后,就会自动停在此处。
    输入指令:u @eip
    输入指令:r

    IofCallDriver函数是FASTCALL类型的调用约定,所以第1个参数的值存储在寄存器ecx中,即873f3508。所以,我们继续查看下该设备对象的结构数据。
    输入指令:dt nt!_DEVICE_OBJECT @ecx

    这时,我们边可以获取到驱动对象的地址DriverObject(0x86f5e670 _DRIVER_OBJECT),继续查看驱动对象的数据内容。
    输入指令:dt nt!_DRIVER_OBJECT 0x86f5e670

    由DriverName中我们可以看出,nt!NtQueryDirectoryFile是将IRP请求包发送给FltMgf驱动程序来处理的。
    3 留言 2019-09-01 09:26:49 奖励20点积分
  • 深度学习 19、DNN

    前文链接:https://write-bug.com/article/2540.html
    深度学习在前几节介绍的模型都属于单点学习模型,比如朴素贝叶斯的模型为概率,lr与softmax的模型为w,b权重,聚类kmeans的模型为中心向量点,还有后面的决策树的树模型等等都属于单点学习模型,都有各自的用途,而这里的深度学习来说,属于机器学习的一部分,可用不同的网络结构来解决各个领域的问题,模型为深度学习网络。
    通过前几节的学习,不知道大家有没有对单点学习的模型有感触,所谓模型是什么?
    我们从已有的例子(训练集)中发现输入x与输出y的关系,这个过程是学习(即通过有限的例子发现输入与输出之间的关系),而我们使用的function就是我们的模型,通过模型预测我们从未见过的未知信息得到输出y,通过激活函数(常见:relu,sigmoid,tanh,swish等)对输出y做非线性变换,压缩值域,而输出y与真实label的差距就是误差error,通过损失函数再求取参数偏导,梯度下降降低误差,从而优化模型,我们前面见过的损失函数是mse和交叉熵

    而DNN的模型就是网络结构,在说如何选取更好的网络结构之前,我们先介绍下神经网络:

    人类大脑的神经网络如上图具象出来就是无数个神经元,而神经元分为几个结构:

    树突:接收信号源
    神经元:核心计算单元
    轴突:传输数据到下一个神经元

    通过一个个神经元的传输,生成结果,比如说条件反射:缩手
    而在我们模型中,这个网络怎么表示呢?

    感知机:最最简单的神经网络,只有一个神经元


    树突:样本特征a1
    轴突:权重w1

    树突与轴突,也就是样本特征和权重相乘,有些类似lr的wx,共同决策了神经元的输入

    神经元:对信号进一步处理,比如lr中wx的∑,并加入偏置b,默认为1
    传递函数(激活函数):对f(s)做压缩非线性变换

    假设一个二分类问题:

    输入:
    树突P1:颜色
    树突P2:形状
    香蕉(黄色,弯形):(-1,-1)
    苹果(红色,圆形):(1,1)

    相当于坐标轴的两个点,我们需要一个分类面把两个点分割开。

    P1颜色的取值为1,-1
    P2形状的取值为1,-1
    初始化w1=w2=1,b=0

    则有:
    s=p1w1+p2w2=2>0s=p2 2w2 2+p2 2w2 2=-2 < 0这个初始化的参数可以区分,但是初始化有随机性,换一组参数就不能区分了
    所以感知机的训练方法,目的就是训练权重和偏置
    w=w+epb=b+ee(误差)=t-a=期望-实际
    如,假设w1=1,w2=-1,b=0

    苹果s=0
    期望为1,e=1-0=1

    修正:
    w1=1+1*1=2w2=-1+1*1=0b=0+1=1s=3>0为苹果但是,y=wx+b只能解决线性可分的问题,对于异或问题,也就是四点对立面,需要一个曲线做分割,而这就意味着需要多层神经元和信号源共同决策分割面,也就形成了复杂的神经网络,而目标:同样是那么多边的权重。

    通过不同层间的结果作为下一层的输入,再对输入中间结果进一步处理,以此类推形成足够的深度,对于大部分问题来说都可以提到一个提升效果的作用。
    感知机只有从输出层具有激活函数,即功能神经元,这里的隐层和输出层都是具有激活函数的功能神经元。
    层与层全连接,同层神经元不连接,不跨层连接的网络又称为多层前馈神经网络。
    前馈:网络拓扑结构上不存在环和回路
    我们通过pytorch实现演示:
    二分类问题:
    假数据准备:
    # make fake data#正态分布随机产生n_data = torch.ones(100, 2)x0 = torch.normal(2*n_data, 1) # class0 x data (tensor), shape=(100, 2)y0 = torch.zeros(100) # class0 y data (tensor), shape=(100, 1)x1 = torch.normal(-2*n_data, 1) # class1 x data (tensor), shape=(100, 2)y1 = torch.ones(100) # class1 y data (tensor), shape=(100, 1)#拼接数据x = torch.cat((x0, x1), 0).type(torch.FloatTensor) # shape (200, 2) FloatTensor = 32-bit floatingy = torch.cat((y0, y1), ).type(torch.LongTensor) # shape (200,) LongTensor = 64-bit integer# tensor类型 :张量:高维特征,不可更新#variable类型:可变参数,更新w,b从而更新xy# torch can only train on Variable, so convert them to Variablex, y = Variable(x), Variable(y)设计构建神经网络:class net 继承torch.nn.model
    200\*2, 2\*10, 10\*2参数:n_fea=2,n_hidden=10,n_output=2
    输入层2
    初始化hidden层10
    self.hidden=torch.nn.linear(n_fea,n_hidden)输出层2
    self.out=torch.nn.linear(n_hidden,n_output)class Net(torch.nn.Module): def __init__(self, n_feature, n_hidden, n_output): super(Net, self).__init__() self.hidden = torch.nn.Linear(n_feature, n_hidden) # hidden layer self.out = torch.nn.Linear(n_hidden, n_output) # output layer#激活函数,在隐藏层后加relu非线性变换 def forward(self, x): x = F.relu(self.hidden(x)) # activation function for hidden layer x = self.out(x) return x #输出的预测值xnet = Net(n_feature=2, n_hidden=10, n_output=2) # define the networkprint(net)参数梯度下降sgd:
    optimizer = torch.optim.sgd(net.parameters(),lr=0.02)损失函数:交叉熵:负对数似然函数,并计算softmax
    loss_func = torch.nn.CrossEntropyLoss()参数初始化:
    optimizer.zero_grad()反向传播:
    loss.backward()计算梯度:
    optimizer.step()for t in range(100): out = net(x) # input x and predict based on x loss = loss_func(out, y) # must be (1. nn output, 2. target), the target label is NOT one-hotted optimizer.zero_grad() # clear gradients for next train loss.backward() # backpropagation, compute gradients optimizer.step() # apply gradients if t % 2 == 0: # plot and show learning process plt.cla() prediction = torch.max(F.softmax(out), 1)[1] pred_y = prediction.data.numpy().squeeze() target_y = y.data.numpy() plt.scatter(x.data.numpy()[:, 0], x.data.numpy()[:, 1], c=pred_y, s=100, lw=0, cmap='RdYlGn') accuracy = sum(pred_y == target_y)/200. plt.text(1.5, -4, 'Accuracy=%.2f' % accuracy, fontdict={'size': 20, 'color': 'red'}) plt.pause(0.1)plt.ioff()plt.show()交叉熵问题:
    二次代价:

    a 是 神经元的输出,其中 a = σ(z), z = wx + b
    C求导:w,b求偏导:
    受到激活函数影响

    交叉熵函数:


    不用对激活函数导数影响,不管是对w求偏导还是对b求偏导,都和e有关,而和激活函数的导数无关,比如说sigmoid函数,只有中间下降最快,两边的下降逐渐缓慢,最后斜率逼近于直线
    反向传播算法:errorBP
    可应用于大部分类型神经网络
    一般说BP算法时,多指多层前馈神经网络
    上面的数据为了演示,造的假数据形成200*2维的矩阵,一列label
    那么如何对接我们的真实数据呢?

    在我们前几节的数据中大都是这种形式,label feature_id:score
    定义一个数组
    output =[None]*3 维护3列tensor数据:
    max_fid = 123num_epochs = 10data_path = '/root/7_codes/pytorch_test/data/a8a'all_sample_size = 0sample_buffer = []with open(data_path, 'r') as fd: for line in fd: all_sample_size += 1 sample_buffer.append(line.strip())output = [None] *3#idids_buffer = torch.LongTensor(all_sample_size, max_fid)ids_buffer.fill_(1)output[0] = ids_buffer#weightweights_buffer = torch.zeros(all_sample_size, max_fid)output[1] = weights_buffer#labellabel_buffer = torch.LongTensor(all_sample_size)output[2] = label_bufferrandom.shuffle(sample_buffer)sample_id = 0for line in sample_buffer: it = line.strip().split(' ') label = int(it[0]) f_id = [] f_w = [] for f_s in it[1:]: f, s = f_s.strip().split(":") f_id.append(int(f)) f_w.append(float(s)) #解析数据后填入数组 f_id_len = len(f_id)#适当拓展数组大小 output[0][sample_id, 0:f_id_len] = torch.LongTensor(f_id) output[1][sample_id, 0:f_id_len] = torch.FloatTensor(f_w) output[2][sample_id] = label sample_id += 1fid, fweight, label = tuple(output)fid, fweight, label = map(Variable, [fid, fweight, label])接下来是设计网络结构:

    def emb_sum(embeds, weights): emb_sum = (embeds * weights.unsqueeze(2).expand_as(embeds)).sum(1) return emb_sum#输入结构为22696条数据*124维特征*32维特征向量:可以将其想象成立方体的三维条边#想要把id的特征向量和对应的score相乘并相加,需要把score也就是权重weight拓展成和id同样的维度,即22696 *124 *32 ,而sum(1)就是把124维相加成1维,最后形成22696×32维的平面矩阵,即每个样本后有个32维的向量存在,相当于把feature_id向量化class Net(torch.nn.Module): def __init__(self, n_embed, n_feature, n_hidden, n_output): super(Net, self).__init__() self.embedding = nn.Embedding(max_fid + 1, n_feature) #124特征*32隐层向量 self.hidden = torch.nn.Linear(n_feature, n_hidden) # hidden layer self.out = torch.nn.Linear(n_hidden, n_output) # output layer def forward(self, fea_ids, fea_weights): embeds = self.embedding(fea_ids) embeds = emb_sum(embeds, fea_weights)#对特征进一步处理,融合id与weight_score进行相容 embeds = nn.functional.tanh(embeds)#非线性变换 hidden = self.hidden(embeds) output = self.out(hidden) return outputnet = Net(n_embed = 32, n_feature= 32, n_hidden=10, n_output=2) # define the networkprint(net)以上,我们对接了真实的数据类型,那这个demo有没有什么问题呢?
    1.是否支持大规模数据,进行快速训练?mini-batch
    我们之前学BGD、SGD、MGD梯度下降的训练方法,在上面就运用了sgd的方法,不管是BGD还是SGD都是对所有样本一次性遍历一次,如果想提升,大致相当于MGD的方法:
    把所有样本分批处理,每批次有多少个样本(batch),循环所有样本循环多少轮(epoch)。
    每训练一个batch产生一条log,参数可保存,之前是每epoch输出一次log,这种方法训练快,性能虽然赶不上全局跑出来的准确率,但仍然一直逼近,小步快跑,每次加载到内存的数据较小,性能方面就高。
    num_epochs=10batch_size =1000data_path = './data/a8a'max_fid = 123def read_and_shuffle(filepath,shuffle=True): lines=[] with open(filepath,’r’) as fd: for linr in fd: lines.append(line.strip()) if shuffle: random.shuffle(lines) return linesclass DataLoader(object): def __init__(self,data_path,batch_size,shuffle = True): self.batch_size = batch_size self.shuffle = shuffle if os.path.isdir(data_path): self.files = [ join_path(data_path,i) for I in os.listdir(data_path)] else: self.files = [data_path] def parse(self,line_ite): for line in line_ite: it = line.strip().split(‘ ’) label = int(it[0]) f_id = [] f_w = [] for f_s in it[1:]: f,s = f_s.strip().split(‘:’) f_id.append(int(f)) f_w.append(float(s)) f_id_len = len(f_id) output = [] output.append(f_id) output.append(f_w) output.append(f_id_len) output.append(label) yield output def to_tensor(self,batch_data,sample_size): output =[None]*3 ids_buffer = torch.LongTensor(sample_size,max_fid) ids_buffer.fill_(1) output[0]=ids_buffer weights_buffer = torch.zeros(sample_size,max_fid) output[1]=weights_buffer label_buffer = torch.LongTensor(sample_size) output[2] = label_buffer for sample_id,sample in enumerate(batch_data): f_id,f_weight,f_id_len,label = sample output[0][sample_id,0:f_id_len] = torch.LongTensor(f_id) output[1][sample_id,0:f_id_len] = torch.FloatTensor(f_w) output[2][sample_id] = label return tuple(output) def batch(self): count = 0 batch_buffer =[] for f in self.files: for parse_res in self.parse(read_and_shuffle(f,self.shuffle)): count+=1 batch_buffer.append(parse_res) if count ==self.batch_size: t_size = len(batch_buffer) batch_tensor = self.to_tensor(batch_buffer,t_size) yield (batch_tensor,t_size) count = 0 batch_buffer = [] if batch_buffer: t_size = len(batch_buffer) batch_tensor = self.to_tensor(batch_buffer,t_size) yield (batch_tensor,t_size)for epoch in range(1,num_epochs+1): for (batch_id,data_tuple) in enumrate(dataloader.batch(),1): data = data_tuple[0] all_sample_size = data_tuple[1] fid,fweight,label =data fid,fweight,label=map(Variable,[fid,fweight,label] optimizer = torch.optim.SGD(net.parameters(), lr=0.02) loss_func = torch.nn.CrossEntropyLoss() # the target label is NOT an one-hotted out = net(fid, fweight) optimizer.zero_grad() # clear gradients for next train loss = loss_func(out, label) loss.backward() # backpropagation, compute gradients optimizer.step() # apply gradients if batch_id % 2 == 0:#每两批次打印一次 # plot and show learning process prediction = torch.max(F.softmax(out), 1)[1] pred_y = prediction.data.numpy().squeeze() target_y = label.data.numpy() accuracy = sum(pred_y == target_y)/float(all_sample_size) print "epoch: %s, batch_id: %s, acc: %s" % (epoch, batch_id, accuracy)2.怎么使用模型?
    现在模型可以训练自己数据了,效果也提升了,那如何把模型的参数保存下来?
    print "epoch: %s, batch_id: %s, acc: %s" % (epoch, batch_id, accuracy) checkpoint = { 'model': net.state_dict(),#网络静态参数 'epoch': epoch, 'batch': batch_id, } model_name = 'epoch_%s_batch_id_%s_acc_%s.chkpt' % (epoch, batch_id, accuracy) model_path = join_path('./model', model_name) torch.save(checkpoint, model_path)拆分上面大文件出各个参数:

    import osimport sysimport torchimport torch.nn as nnimport randomfrom torch.autograd import Variablefrom os.path import join as join_pathimport torch.nn.functional as Fmodel_path = sys.argv[1]checkpoint = torch.load(model_path)model_dict = checkpoint['model']for k, v in model_dict.items(): print k model_sub_file_name = k model_sub_file_path = join_path('./dump', model_sub_file_name) f = open(model_sub_file_path, 'w') value = model_dict[k].cpu().numpy() value.dump(f) f.close()模型怎么用?加载、预测
    import sysimport numpy as npimport mathembedding_weight = np.load('dump/embedding.weight')hidden_weight = np.load('./dump/hidden.weight')hidden_bias = np.load('./dump/hidden.bias')out_weight = np.load('./dump/out.weight')out_bias = np.load('./dump/out.bias')emb_dim = embedding_weight.shape[1]def calc_score(fids, fweights): embedding = np.zeros(emb_dim) for id, weight in zip(fids, fweights): embedding += weight * embedding_weight[id] embedding_tanh = np.tanh(embedding)#对应相乘,第二维相加成10维向量 hidden = np.sum(np.multiply(hidden_weight, embedding_tanh), axis=1) + hidden_bias out = np.sum(np.multiply(out_weight, hidden), axis=1) + out_bias return outdef softmax(x): exp_x = np.exp(x) softmax_x = exp_x / np.sum(exp_x) return softmax_xfor line in sys.stdin: ss = line.strip().split(' ') label = ss[0].strip() fids = [] fweights = [] for f_s in ss[1:]: f, s = f_s.strip().split(':') fids.append(int(f)) fweights.append(float(s)) pred_label = softmax(calc_score(fids, fweights))[1] print label, pred_label3.怎么评估?auc
    cat auc.raw | sort -t$'\t' -k2g | awk -F'\t' '($1==-1){++x;a+=y}($1==1){++y}END{print 1.0 - a/(x*y)}'acc=0.827auc=0.842569acc=0.745auc=0.494206轮数、acc都影响着auc,数字仅供参考
    总结以上,是以二分类为例,从头演示了一遍神经网络,大家可再找一些0-9手写图片分类任务体验一下,这里总结下,模型拥有参数,所以涉及参数初始化,有了参数后涉及正向传递,分为拟合和泛化两部分建立好模型后,学习过程就是确定参数的过程,使用的是梯度下降法,定义一个误差函数loss function 来衡量模型预测值与真实值(label)之间的差距,通过不断降低差距来使模型预测值逼近真实值,更新参数的反向传递是利用对应的误差值来求得梯度(batch、epoch、learning rate 、shuffle),评估标准也根据任务类型和具体要求可以使用更多的指标。
    回归任务常用均方差MSE作为误差函数。评估可用均方差表示。
    分类任务常用交叉熵(cross entropy)和softmax配合使用。评估可用准确率表示。
    回归任务:

    借用知乎yjango末尾的一句话:
    但最重要的并非训练集的准确率,而是模型从未见过的测试集的准确率(泛化能力),正如高考,真正的目的是为了解出从未见过的高考题,而不是已经做过的练习题,学习的困难之处正是要用有限的样本训练出可以符合无限同类样本规律的模型。是有限同无限的对抗,你是否想过,为什么看了那么多的道理、听了那么多人生讲座,依然过不哈这一生,一个原因就在于演讲者向你展示的例子都是训练集的样本,他所归纳的方法可以很轻松的完美拟合这些样本,但未必对你将面临的新问题同样奏效,人的一生都在学习,都在通过观察有限的例子找出问题和答案的规律,中医是,玄学是,科学同样是,但科学,是当中最为可靠的一种。
    1 留言 2019-06-05 14:38:57 奖励18点积分
  • windows下静态使用QxOrm框架并使用泛型编程 (一)

    开发环境
    Qt 5.6 for Desktop
    QxOrm版本为1.46 下载链接https://github.com/QxOrm/QxOrm

    下载完成以后QxOrm文件夹里会有简单的说明文档doc/index.html 不过有些方法是比较过时的,于是我测试了许久才测试好的框架代码。
    下载完成后打开QtCreator,选择新建项目选择 其他项目/子目录项目 类似下图的建立

    然后进入该项目的文件夹路径即200-300.pro所在路径 将QxOrm整个文件夹复制进来
    接着在200-300.pro文件中改为
    TEMPLATE = subdirsSUBDIRS += \ QxOrm这样就可以看到QxOrm项目成功加入到整体项目中了。而程序需要主项目作为运行所以我们右键200-300新建一个Application/Qt Widgets Application 我这边就叫Demo好了 。接着右键Demo添加库/外部库 库文件选择QxOrm/lib/libQxOrm.a 链接选择静态然后点下一步完成
    注意:需要在Demo.pro文件中加入QT += sql 这句话是将数据库加入到项目中去。
    目前环境就算是搭好了。下一篇继续讲如何使用。
    1 留言 2019-07-23 22:47:43 奖励8点积分
  • 【Cocos Creator实战教程(4)】——炸弹人(TiledMap相关) 精华

    1. 相关知识点
    虚拟手柄(新)
    地图制作
    碰撞检测
    动画制作

    2. 步骤2.1 制作地图2.1.1.新建19x19的地图,Tile大小32x32,导入图块资源2.1.2 建立三个图层(ground,hide,main)和一个对象层(objects)ground是背景层,用绿色的草坪图块填充满

    hide是道具层,挑选道具图块(包括出口的门)放在几个位置

    main是障碍物层,还原经典炸弹人的地图布局,每隔一个位置放一个钢块,用土块覆盖道具层的道具,在其他位置再放几个土块

    objects层有两个对象(player,enemy)标记玩家和敌人的位置信息
    最终效果如图

    2.1.3 将除了草坪图块的其他图块添加type属性,属性值分别为,soil(土块),steel(钢块),door(门),prop1(道具1)…
    2.1.4 保存地图文件和图集文件放在一起2.2 绑定地图2.2.1 新建工程Bomberman,将所需资源全部导入,项目结构如下(有些是后来添加的)
    2.2.2 将地图拖入场景(自动生成map和layer节点),将锚点改为(0,0),下面出现的player等节点也都是(0,0)为锚点2.2.3 新建脚本game.js添加为map节点组件,声明全局变量,在属性面板中拖入相关层节点
    2.3 虚拟手柄2.3.1 我们准备了上下左右炸弹五个按钮的原始状态和按下状态共十张图片素材,在场景编辑器中看着顺眼的地方摆放这五个按钮(将label删除),并将按钮的相应状态的按钮图片添加到右侧属性面板中2.3.2 为每个按钮添加点击事件,(map->game->btnBomb, btnUp, btnDown, btnLeft, btnRight)
    2.4 炸弹动画2.4.1 将炸弹图片集中的任意一个拖入场景中,将其宽高改为32x32,再拖回资源管理器中制作一个炸弹的prefab bomb,为其添加Animation组件2.4.2 新建炸弹爆炸动画clip explode,拖入bomb的属性面板2.4.3 在动画播放完成的最后一帧加入一个帧事件,响应事件方法名为exploded
    2.5 主角和敌人2.5.1 添加player和enemy为map节点的两个子节点,锚点0,0,大小32x32 (位置随意,一会我们要在代码中动态设置他们的位置)
    3. 总结
    虚拟手柄是一个比较热点的控制,现实生活中还会有圆圈控制各个角度的,也有开源项目大家可以参考
    注意节点前后层的遮挡关系

    注意:本教程部分素材来源于网络,另请大家在评论区踊跃提问发言。
    7 留言 2018-12-20 12:16:05 奖励20点积分
  • 机器学习 23 、BM25 Word2Vec

    前文链接:https://write-bug.com/article/2689.html
    word embedding什么是word embedding? 简单的来说就是将一个特征转换为一个向量。在推荐系统当中,我们经常会遇到离散特征,如userid、itemid。对于离散特征,我们一般的做法是将其转换为one-hot,但对于itemid这种离散特征,转换成one-hot之后维度非常高,但里面只有一个是1,其余都为0。这种情况下,我们的通常做法就是将其转换为embedding。
    word embedding为什么翻译成词嵌入模型?一个向量可以想象成多维空间,那么在多维空间里面找到一个点来代表这个词,相当于把这个词嵌入到了多维空间里,所以可以称为词嵌入模型。
    one-hot编码形式
    输入:
    apple on a apple tree – Bag of Words: ["apple", "on", "a", "tree"]– Word Embedding的输出就是每个word的向量表示
    最简单直接的是one-hot编码形式:那么每个word都对应了一种数值表示
    例如,apple对应的vector就是[1, 0, 0, 0],a对应的vector就是[0, 0, 1, 0]
    两类表达方式:基于频率:(本质:one-hot)
    Count vector:需要穷举所有单词,形成非常庞大、稀疏的矩阵,穷举时,计算每词的词频TF
    每个文档用词向量的组合来表示,每个词的权重用其出现的次数来表示
    假设语料库内容如下:
    – D1: He is a boy. – D2: She is a girl, good girl.那么可以构建如下2 × 7维的矩阵

    tfidf vector:同时考虑了TF与IDF
    IDF=log(N/n)。 – N代表语料库中文档的总数 – n代表某个word在几个文档中出现过;
    Co-Occurrence vector:只考虑滑动窗口内单词的共现,等于考虑了上下文之间的关系
    前面两类方法,每个字都是独立的,缺少了语义的学习
    例如: He is not lazy. He is intelligent. He is smart.
    如果Context Window大小为2,那么可以得到如下的共现矩阵:

    he和is计算方法(窗口共现):

    基于模型:
    CBOW
    Skip-Gram
    技术升级TF-IDF-》BM25
    Co-Occurrence-》WordtoVector
    BM25:定位:搜索引擎相关性评分
    通常我们在搜索信息时,搜索的句子为query,关联出的排序列表为item,即:
    query -》item
    那么可分词query-》token1 token2 token3
    同时可把item -》doc-》token token token
    词在文章中的评分:token1 -》doc :score1(tfidf)
    所以相关性评分query-》item:score1+score2+…
    公式可表示为:
    S(q,d)=∑Wi*R(qi,d)Wi用idf表示:

    N为索引中的全部文档数
    n(qi)为包含了qi的文档数
    相关性评分R:


    k1,k2,b为调节因子,一般k1=2,b=0.75
    b是调整文档长度对相关性影响的大小 • b越大,文档长度影响R越大,反之越小


    fi为qi在d中的出现频率 =TF
    qfi为qi在Query中的出现频率 =query中的TF

    在之前的tfidf中TF越大最后的分数也越大,而通常情况下,文章越长,TF大的可能性越大,没有考虑到文章长度的问题,这里BM25考虑到了这个问题,如果文章过长就要给定一定的打压,那么如何界定文章长度呢?除以avgdl,即所有文章平均长度

    dl为文档d的长度
    avgdl为所有文档的平均长度

    公式简化:
    绝大部分情况下,qi在Query中只会出现一次,即qfi=1


    BM25实践:
    1.gensim word2vec
    语料库-》每个词的50维向量即word embedding
    from gensim.models import word2vecmodel=word2vec.Word2Vec(sentences,size=50,window=5,min_count=1,workers=6)model.wv.save_word2vec_format(file_voc, binary=False)2.coumpute_idf
    每个词的idf
    file_corpus='../data/file_corpus.txt'file_voc='../data/voc.txt'file_idf='../data/idf.txt'class ComIdf(object): def __init__(self,file_corpus,file_voc,file_idf): self.file_corpus=file_corpus self.file_voc=file_voc self.file_idf=file_idf self.voc=load_voc(self.file_voc) self.corpus_data=self.load_corpus() self.N=len(self.corpus_data) def load_corpus(self): input_data = codecs.open(self.file_corpus, 'r', encoding='utf-8') return input_data.readlines() def com_idf(self,word): n = 0 for _,line in enumerate(self.corpus_data): n+=line.count(word) idf=math.log(1.0*self.N/n+1) return {word:idf} def parts(self): words=set(self.voc.keys()) multiprocessing.freeze_support() cores=multiprocessing.cpu_count() pool=multiprocessing.Pool(processes=cores-2) reuslt=pool.map(self.com_idf,words) idf_dict=dict() for r in reuslt: k=list(r.keys())[0] v=list(r.values())[0] idf_dict[k]=idf_dict.get(k,0)+v with codecs.open(self.file_idf,'w',encoding='utf-8') as f:f.write(json.dumps(idf_dict,ensure_ascii=False,indent=2,sort_keys=False))if __name__ == '__main__': t1 = time.time() IDF=ComIdf(file_corpus,file_voc,file_idf)IDF.parts()3.get_sentence
    分割句子,取出最常见句子1w个
    def get_sentence(): file_corpus=codecs.open('../data/file_corpus.txt','r',encoding='utf-8') file_sentence=codecs.open('../data/file_sentence.txt','w',encoding='utf-8') st=dict() for _,line in enumerate(file_corpus): line=line.strip() blocks=re_han.split(line) for blk in blocks: if re_han.match(blk) and len(blk)>10: st[blk]=st.get(blk,0)+1 st=sorted(st.items(),key=lambda x:x[1],reverse=True) for s in st[:10000]: file_sentence.write(s[0]+'\n') file_corpus.close() file_sentence.close()get_sentence()4.test
    各种算法计算打分:cos\idf\bm25\jaccard
    file_voc='./data/voc.txt'file_idf='./data/idf.txt'file_userdict='./data/medfw.txt'class SSIM(object): def __init__(self): t1 = time.time() self.voc=load_voc(file_voc) print("Loading word2vec vector cost %.3f seconds...\n" % (time.time() - t1)) t1 = time.time() self.idf=load_idf(file_idf) print("Loading idf data cost %.3f seconds...\n" % (time.time() - t1)) jieba.load_userdict(file_userdict) def M_cosine(self,s1,s2): s1_list=jieba.lcut(s1) s2_list=jieba.lcut(s2) v1=np.array([self.voc[s] for s in s1_list if s in self.voc]) v2=np.array([self.voc[s] for s in s2_list if s in self.voc]) v1=v1.sum(axis=0) v2=v2.sum(axis=0) sim=1-spatial.distance.cosine(v1,v2) return sim def M_idf(self,s1, s2): v1, v2 = [], [] s1_list = jieba.lcut(s1) s2_list = jieba.lcut(s2) for s in s1_list: idf_v = self.idf.get(s, 1) if s in self.voc: v1.append(1.0 * idf_v * self.voc[s]) for s in s2_list: idf_v = self.idf.get(s, 1) if s in self.voc: v2.append(1.0 * idf_v * self.voc[s]) v1 = np.array(v1).sum(axis=0) v2 = np.array(v2).sum(axis=0) sim = 1 - spatial.distance.cosine(v1, v2) return sim def M_bm25(self,s1, s2, s_avg=10, k1=2.0, b=0.75): bm25 = 0 s1_list = jieba.lcut(s1) for w in s1_list: idf_s = self.idf.get(w, 1) bm25_ra = s2.count(w) * (k1 + 1) bm25_rb = s2.count(w) + k1 * (1 - b + b * len(s2) / s_avg) bm25 += idf_s * (bm25_ra / bm25_rb) return bm25 def M_jaccard(self,s1, s2): s1 = set(s1) s2 = set(s2) ret1 = s1.intersection(s2) ret2 = s1.union(s2) jaccard = 1.0 * len(ret1)/ len(ret2) return jaccard def ssim(self,s1,s2,model='cosine'): if model=='idf': f_ssim=self.M_idf elif model=='bm25': f_ssim=self.M_bm25 elif model=='jaccard': f_ssim=self.M_jaccard else: f_ssim = self.M_cosine sim=f_ssim(s1,s2) return simsm=SSIM()ssim=sm.ssimdef test(): test_data=[u'临床表现及实验室检查即可做出诊断', u'面条汤等容易消化吸收的食物为佳', u'每天应该摄入足够的维生素A', u'视患者情况逐渐恢复日常活动', u'术前1天开始预防性运用广谱抗生素'] model_list=['cosine','idf','bm25','jaccard'] file_sentence=codecs.open('./data/file_sentence.txt','r',encoding='utf-8') train_data=file_sentence.readlines() for model in model_list: t1 = time.time() dataset=dict() result=dict() for s1 in test_data: dataset[s1]=dict() for s2 in train_data: s2=s2.strip() if s1!=s2: sim=similarity.ssim(s1,s2,model=model) dataset[s1][s2]=dataset[s1].get(s2,0)+sim for r in dataset: top=sorted(dataset[r].items(),key=lambda x:x[1],reverse=True) result[r]=top[0] with codecs.open('./data/test_result.txt','a') as f: f.write('--------------The result of %s method------------------\n '%model) f.write('\tThe computing cost %.3f seconds\n'% (time.time() - t1)) f.write(json.dumps(result, ensure_ascii=True, indent=2, sort_keys=False)) f.write('\n\n') file_sentence.close()test()word2vec继承了之前的窗口滑动思想,得到更好的语义理解
    涉及的技术点:
    – Hierarchical softmax分层softmax
    – Negative sampling负采样
    两种方式:

    CBOW:根据前后词,预测中间词
    Skip-Gram :根据中间词预测前后词,工业用的更多

    CBOW一句话包含了很多单词,时间窗口选择到了一个中间词,w3,假设左边右边各学习两个,中间词假设不知道,那么根据w1245猜测中间这个w3词,故意装作w3不知道做预测,那么w3就是中心词label,w1245就是周围词训练数据,随着时间窗口滑动,同理w4又继续成为了中间词,那么由此类推形成了很多训练样本


    Input layer:由one-hot编码的输入上下文{x1,…,xC}组成, 其中窗口大小为C,词汇表大小为V
    Hidden layer:隐藏层是N维的向量
    Output layer:被one-hot编码的输出单词y

    被one-hot编码的输入向量通过一个V×N维的权重矩阵W连接到隐藏层; 隐藏层通过一个N×V的权重矩阵W′连接到输出层
    不管输入多少词,相加并都映射为中间得隐层向量上,再做一个softmax输出
    Skip-Gram:CBOW的网络翻转
    样本怎么得到的?

    input word和output word都是one-hot编码的向量。最终模型的输出是一个概率分布

    softmax分类需要所有单词:这里是50002个单词,需要遍历一遍,那么每次计算都需要一定时间,不管是多次还是一次时间成本都很高,所以引出了分层softmax:
    Hierarchical softmax哈夫曼树

    是二叉树的一种特殊形式,又称为最优二叉树,其主要作用在于数据压缩和编码长度的优化
    定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度(WPL)达到最小,称这样的二叉 树为最优二叉树,也称为霍夫曼树(Huffman Tree)
    WPL:树的带权路径长度,所有叶子结点的带权路径长度之和

    让wpl最小,压缩数据信息
    主要用于编码:从根节点出发,向左为0,向右为1
    等长编码和哈夫曼编码


    生成:
    每个节点按权重大小从小到大排序
    两个最小的为叶子节点相加成为父节点并和其他最小的成两个叶子节点,最终形成哈夫曼树
    之前做softmax,50002个单词需要从头到尾计算一遍,把词表做成树,每个叶子节点都是一个单词,树的每个结点都是一个sigmoid,来了一个单词做一个lr二分类向左还是向右分类直至最后叶子节点为所要的词
    Negative sampling负采样
    思想:每次让一个训练样本仅更新一小部分权重
    需要离错误的样本更远

    不对所有输出神经元对应的权值进行更新,只是随机选取几个“negative”词,更新他们对应的权重。当然, 我们也会更新”positive”的对应权值
    随机取的样本只要不共现就是负样本,概率大的会多选,即出现多的单词自然被取出的概率越大
    实际论文实现:


    CBOW:input是context(周围词)而output是中心词,训练过程中其实是在从output的loss学习周围词的信息 也就是embedding,但是在中间层是average的,一共预测V(vocab size)次就够了
    一次softmax

    Skipgram:用中心词预测周围词,预测的时候是一对word pair,等于对每一个中心词都有K个词作为output, 对于一个词的预测有K次,所以能够更有效的从context中学习信息,但是总共预测K*V词
    多次softmax

    Skip-Gram的训练时间更长,更准确
    Cbow训练速度快,Skip-Gram更好处理生僻字

    Word2Vec实践:
    import sysimport torchimport torch.nn as nnimport torch.nn.functional as Fimport torch.utils.data as tudfrom torch.nn.parameter import Parameterfrom collections import Counterimport numpy as npimport randomimport mathimport pandas as pdimport scipyimport sklearnfrom sklearn.metrics.pairwise import cosine_similarity# 超参数K = 100 # number of negative samplesC = 3 # nearby words thresholdNUM_EPOCHS = 2 # The number of epochs of trainingMAX_VOCAB_SIZE = 30000 # the vocabulary sizeBATCH_SIZE = 128 # the batch sizeLEARNING_RATE = 0.2 # the initial learning rateEMBEDDING_SIZE = 100# 分词def word_tokenize(text): return text.split()with open("./data/text8", "r") as fin: text = fin.read()text = [w for w in word_tokenize(text.lower())]print(text[1])vocab = dict(Counter(text).most_common(MAX_VOCAB_SIZE - 1))#计数器,取前30000个高频元素vocab["<unk>"] = len(text) - np.sum(list(vocab.values()))print (np.sum(list(vocab.values())))print (len(text))print (vocab["<unk>"])print (len(vocab))#output:originated170052071700520769010330000idx_to_word = [word for word in vocab.keys()]word_to_idx = {word: i for i, word in enumerate(idx_to_word)}word_counts = np.array([count for count in vocab.values()], dtype=np.float32)word_freqs = word_counts / np.sum(word_counts)word_freqs = word_freqs ** (3. / 4.)word_freqs = word_freqs / np.sum(word_freqs) # 用来做 negative samplingVOCAB_SIZE = len(idx_to_word)print(VOCAB_SIZE)#text 每个单词#word_to_idx 单词id化:dict#idx_to_word 每个单词#word_freqs 负采样概率#word_counts 单词计数列表#数据样本列举:class WordEmbeddingDataset(tud.Dataset): def __init__(self, text, word_to_idx, idx_to_word, word_freqs, word_counts): ''' text: a list of words, all text from the training dataset word_to_idx: the dictionary from word to idx idx_to_word: idx to word mapping word_freq: the frequency of each word word_counts: the word counts ''' super(WordEmbeddingDataset, self).__init__() self.text_encoded = [word_to_idx.get(t, VOCAB_SIZE - 1) for t in text] self.text_encoded = torch.Tensor(self.text_encoded).long() self.word_to_idx = word_to_idx self.idx_to_word = idx_to_word self.word_freqs = torch.Tensor(word_freqs) self.word_counts = torch.Tensor(word_counts) def __len__(self): ''' 返回整个数据集(所有单词)的长度 ''' return len(self.text_encoded) def __getitem__(self, idx): ''' 这个function返回以下数据用于训练 - 中心词 - 这个单词附近的(positive)单词 - 随机采样的K个单词作为negative sample ''' center_word = self.text_encoded[idx] pos_indices = list(range(idx - C, idx)) + list(range(idx + 1, idx + C + 1)#窗口左3右3 pos_indices = [i % len(self.text_encoded) for i in pos_indices]#去除类似第一次左边无词这种情况 pos_words = self.text_encoded[pos_indices] neg_words = torch.multinomial(self.word_freqs, K * pos_words.shape[0], True) return center_word, pos_words, neg_wordsdataset = WordEmbeddingDataset(text, word_to_idx, idx_to_word, word_freqs, word_counts)dataloader = tud.DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=4)
    class EmbeddingModel(nn.Module): def __init__(self, vocab_size, embed_size): ''' 初始化输出和输出embedding ''' super(EmbeddingModel, self).__init__() self.vocab_size = vocab_size self.embed_size = embed_size initrange = 0.5 / self.embed_size self.out_embed = nn.Embedding(self.vocab_size, self.embed_size, sparse=False) self.out_embed.weight.data.uniform_(-initrange, initrange) self.in_embed = nn.Embedding(self.vocab_size, self.embed_size, sparse=False) self.in_embed.weight.data.uniform_(-initrange, initrange) def forward(self, input_labels, pos_labels, neg_labels): ''' input_labels: 中心词, [batch_size] pos_labels: 中心词周围 context window 出现过的单词 [batch_size * (window_size * 2)] neg_labelss: 中心词周围没有出现过的单词,从 negative sampling 得到 [batch_size, (window_size * 2 * K)] return: loss ''' input_embedding = self.in_embed(input_labels) # B * embed_size pos_embedding = self.out_embed(pos_labels) # B * (2*C) * embed_size neg_embedding = self.out_embed(neg_labels) # B * (2*C * K) * embed_size print("input_embedding size:", input_embedding.size()) print("pos_embedding size:", pos_embedding.size()) print("neg_embedding size:", neg_embedding.size()) log_pos = torch.bmm(pos_embedding, input_embedding.unsqueeze(2)).squeeze() # B * (2*C) log_neg = torch.bmm(neg_embedding, -input_embedding.unsqueeze(2)).squeeze() # B * (2*C*K)先拓展再压缩 print("log_pos size:", log_pos.size()) print("log_neg size:", log_neg.size()) log_pos = F.logsigmoid(log_pos).sum(1)#1为横向压缩,2为竖向压缩,可以试试玩 log_neg = F.logsigmoid(log_neg).sum(1) loss = log_pos + log_neg print("log_pos size:", log_pos.size()) print("log_neg size:", log_neg.size()) print("loss size:", loss.size()) return -loss def input_embeddings(self): return self.in_embed.weight.data.cpu().numpy()model = EmbeddingModel(VOCAB_SIZE, EMBEDDING_SIZE)if USE_CUDA: model = model.cuda()if __name__ == '__main__': optimizer = torch.optim.SGD(model.parameters(), lr=LEARNING_RATE) for e in range(NUM_EPOCHS): for i, (input_labels, pos_labels, neg_labels) in enumerate(dataloader): print(input_labels.size()) print(pos_labels.size()) print(neg_labels.size()) input_labels = input_labels.long() pos_labels = pos_labels.long() neg_labels = neg_labels.long() if USE_CUDA: input_labels = input_labels.cuda() pos_labels = pos_labels.cuda() neg_labels = neg_labels.cuda() optimizer.zero_grad() loss = model(input_labels, pos_labels, neg_labels).mean() loss.backward() optimizer.step() if i % 100 == 0: with open(LOG_FILE, "a") as fout: fout.write("epoch: {}, iter: {}, loss: {}\n".format(e, i, loss.item())) print("epoch: {}, iter: {}, loss: {}".format(e, i, loss.item())) embedding_weights = model.input_embeddings() torch.save(model.state_dict(), "embedding-{}.th".format(EMBEDDING_SIZE))使用场景:
    1.item title检索 +ANN+word avg,word average: – 对所有输入词向量求和并取平均 – 例如:输入三个4维词向量:(1,2,3,4),(9,6,11,8),(5,10,7,12) – 那么我们word2vec映射后的词向量就是(5,6,7,8)
    2.模型初始化:处理特征等
    3.近义词(语义理解):可通过计算词向量的加减,与相似度可进行语义理解
    实践例:
    def evaluate(filename, embedding_weights): if filename.endswith(".csv"): data = pd.read_csv(filename, sep=",") else: data = pd.read_csv(filename, sep="\t") human_similarity = [] model_similarity = [] for i in data.iloc[:, 0:2].index: word1, word2 = data.iloc[i, 0], data.iloc[i, 1] if word1 not in word_to_idx or word2 not in word_to_idx: continue else: word1_idx, word2_idx = word_to_idx[word1], word_to_idx[word2] word1_embed, word2_embed = embedding_weights[[word1_idx]], embedding_weights[[word2_idx]] model_similarity.append(float(sklearn.metrics.pairwise.cosine_similarity(word1_embed, word2_embed))) human_similarity.append(float(data.iloc[i, 2])) return scipy.stats.spearmanr(human_similarity, model_similarity)# , model_similaritydef find_nearest(word): index = word_to_idx[word] embedding = embedding_weights[index] cos_dis = np.array([scipy.spatial.distance.cosine(e, embedding) for e in embedding_weights])#1-cosine return [idx_to_word[i] for i in cos_dis.argsort()[:10]]model.load_state_dict(torch.load("model/embedding-{}.th".format(EMBEDDING_SIZE), map_location='cpu'))embedding_weights = model.input_embeddings()# 找相似的单词for word in ["good", "computer", "china", "mobile", "study"]: print(word, find_nearest(word))# 单词之间的关系,寻找nearest neighborsman_idx = word_to_idx["man"]king_idx = word_to_idx["king"]woman_idx = word_to_idx["woman"]embedding = embedding_weights[woman_idx] - embedding_weights[man_idx] + embedding_weights[king_idx]cos_dis = np.array([scipy.spatial.distance.cosine(e, embedding) for e in embedding_weights])for i in cos_dis.argsort()[:20]: print(idx_to_word[i])
    1 留言 2019-06-23 15:52:45 奖励13点积分
显示 75 到 90 ,共 15 条
eject