TranE是一篇Bordes等人2013年发表在NIPS上的文章提出的算法。它的提出,是为了解决多关系数据(multi-relational data)的处理问题。TransE的直观含义,就是TransE基于实体和关系的分布式向量表示,将每个三元组实例(head,relation,tail)中的关系relation看做从实体head到实体tail的翻译,通过不断调整h、r和t(head、relation和tail的向量),使(h + r) 尽可能与 t 相等,即 h + r = t。
这篇文章主要用来记录下TransE的代码。代码难点有两点,一是生成随机数的过程相对复杂一些;第二是生成伪数据时的流程即corrupt_head,其他照着主函数的执行流程应该都没问题。附一张corrupt_head执行样例。

#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <string>
#include <algorithm>
#include <pthread.h>
#include <iostream>
#include <sstream>using namespace std;const float pi = 3.141592653589793238462643383;int transeThreads = 8;
int transeTrainTimes = 1000;
int nbatches = 10;
int dimension = 50;
float transeAlpha = 0.001;
float margin = 1;string inPath = "../../";
string outPath = "../../";int *lefHead, *rigHead;
int *lefTail, *rigTail;struct Triple {int h, r, t;
};Triple *trainHead, *trainTail, *trainList;struct cmp_head {bool operator()(const Triple &a, const Triple &b) {return (a.h < b.h)||(a.h == b.h && a.r < b.r)||(a.h == b.h && a.r == b.r && a.t < b.t);}
};struct cmp_tail {bool operator()(const Triple &a, const Triple &b) {return (a.t < b.t)||(a.t == b.t && a.r < b.r)||(a.t == b.t && a.r == b.r && a.h < b.h);}
};/*There are some math functions for the program initialization.*/
// 转换数组next_random中index为id的值
unsigned long long *next_random;
// 转换next_random索引为id的值并返回
unsigned long long randd(int id) {next_random[id] = next_random[id] * (unsigned long long)25214903917 + 11;return next_random[id];
}int rand_max(int id, int x) { //小于x的随机数int res = randd(id) % x;while (res<0)res+=x;return res;
}float rand(float min, float max) {return min + (max - min) * rand() / (RAND_MAX + 1.0);
}// 返回x的概率密度函数
float normal(float x, float miu,float sigma) {return 1.0/sqrt(2*pi)/sigma*exp(-1*(x-miu)*(x-miu)/(2*sigma*sigma));
}//返回一个大于或等于均值miu的概率密度并且属于[min,max]的数
float randn(float miu,float sigma, float min ,float max) {float x, y, dScope;do {x = rand(min,max);y = normal(x,miu,sigma);dScope=rand(0.0,normal(miu,miu,sigma));} while (dScope > y);return x;
}
// 向量标准化
void norm(float * con) {float x = 0;for (int  ii = 0; ii < dimension; ii++)x += (*(con + ii)) * (*(con + ii));x = sqrt(x);if (x>1)for (int ii=0; ii < dimension; ii++)*(con + ii) /= x;
}/*Read triples from the training file.*/int relationTotal, entityTotal, tripleTotal;
float *relationVec, *entityVec;
float *relationVecDao, *entityVecDao;// 将实体id 关系id 三元组导入、初始化要训练的向量
void init() {FILE *fin;int tmp;fin = fopen((inPath + "relation2id.txt").c_str(), "r"); //fopen创建或打开,c_str()返回一个指向正规C字符串的临时的指针常量tmp = fscanf(fin, "%d", &relationTotal); //获取总的total数fclose(fin);//构建关系向量relationVec = (float *)calloc(relationTotal * dimension, sizeof(float));//分配realtionTotal*dimensionfor (int i=0;i<relationTotal; i++) {for (int ii=0;ii<dimension; ii++)relationVec[i*dimension+ii] = randn(0,1.0/dimension,-6/sqrt(dimension),6/sqrt(dimension));//(miu,sigma,min,max)}fin = fopen((inPath + "entity2id.txt").c_str(), "r");tmp = fscanf(fin,"%d",&entityTotal);fclose(fin);entityVec = (float *)calloc(entityTotal * dimension, sizeof(float));for (int i=0;i<entityTotal;i++) {for (int ii=0;ii<dimension;ii++)entityVec[i * dimension + ii] = randn(0, 1.0 / dimension, -6 / sqrt(dimension), 6 / sqrt(dimension));norm(entityVec+i*dimension); // 单个entity向量标准化}fin = fopen((inPath + "triple2id.txt").c_str(), "r");tmp = fscanf(fin, "%d", &tripleTotal);trainHead = (Triple *)calloc(tripleTotal, sizeof(Triple));trainTail = (Triple *)calloc(tripleTotal, sizeof(Triple));trainList = (Triple *)calloc(tripleTotal, sizeof(Triple));tripleTotal = 0;//trainlist存储三元组,复制给 trainHead和trainTailwhile (fscanf(fin, "%d", &trainList[tripleTotal].h) == 1) {tmp = fscanf(fin, "%d", &trainList[tripleTotal].t);tmp = fscanf(fin, "%d", &trainList[tripleTotal].r);trainHead[tripleTotal].h = trainList[tripleTotal].h;trainHead[tripleTotal].t = trainList[tripleTotal].t;trainHead[tripleTotal].r = trainList[tripleTotal].r;trainTail[tripleTotal].h = trainList[tripleTotal].h;trainTail[tripleTotal].t = trainList[tripleTotal].t;trainTail[tripleTotal].r = trainList[tripleTotal].r;tripleTotal++;}fclose(fin);//按照head和tail排序sort(trainHead, trainHead + tripleTotal, cmp_head());sort(trainTail, trainTail + tripleTotal, cmp_tail());lefHead = (int *)calloc(entityTotal, sizeof(int));rigHead = (int *)calloc(entityTotal, sizeof(int));lefTail = (int *)calloc(entityTotal, sizeof(int));rigTail = (int *)calloc(entityTotal, sizeof(int));memset(rigHead, -1, sizeof(int)*entityTotal); //初始化memset(rigTail, -1, sizeof(int)*entityTotal);for (int i=1;i<tripleTotal;i++) {if (trainTail[i].t != trainTail[i - 1].t) {rigTail[trainTail[i - 1].t] = i - 1;  // 将索引为i-1的t的值置为i-1,意思TrainTail[i-1]的值的终止点lefTail[trainTail[i].t] = i; // 将索引为i的t的值置为i,意思是TrainTail[i]与左侧值不同,意思TrainTail[i]的值的终止点}if (trainHead[i].h != trainHead[i - 1].h) {rigHead[trainHead[i - 1].h] = i - 1;lefHead[trainHead[i].h] = i;}}rigHead[trainHead[tripleTotal - 1].h] = tripleTotal - 1;rigTail[trainTail[tripleTotal - 1].t] = tripleTotal - 1;relationVecDao = (float*)calloc(dimension * relationTotal, sizeof(float));entityVecDao = (float*)calloc(dimension * entityTotal, sizeof(float));
}/*Training process of transE.*/int transeLen;
int transeBatch;
float res;// 计算距离 d(e1-e2-r)=sum(|e1-e2-r|)
float calc_sum(int e1, int e2, int rel) {float sum=0;int last1 = e1 * dimension;int last2 = e2 * dimension;int lastr = rel * dimension;for (int ii=0; ii < dimension; ii++) {// 从entityVec取值计算losssum += fabs(entityVec[last2 + ii] - entityVec[last1 + ii] - relationVec[lastr + ii]);}return sum;
}
// 更新梯度,正样本试图缩小梯度,负样本试图增大梯度
void gradient(int e1_a, int e2_a, int rel_a, int e1_b, int e2_b, int rel_b) {int lasta1 = e1_a * dimension;int lasta2 = e2_a * dimension;int lastar = rel_a * dimension;int lastb1 = e1_b * dimension;int lastb2 = e2_b * dimension;int lastbr = rel_b * dimension;for (int ii=0; ii  < dimension; ii++) {float x;x = (entityVec[lasta2 + ii] - entityVec[lasta1 + ii] - relationVec[lastar + ii]);if (x > 0)x = -transeAlpha;elsex = transeAlpha;relationVec[lastar + ii] -= x;entityVec[lasta1 + ii] -= x;entityVec[lasta2 + ii] += x;x = (entityVec[lastb2 + ii] - entityVec[lastb1 + ii] - relationVec[lastbr + ii]);if (x > 0)x = transeAlpha;elsex = -transeAlpha;relationVec[lastbr + ii] -=  x;entityVec[lastb1 + ii] -= x;entityVec[lastb2 + ii] += x;}
}// 计算距离并更新梯度
void train_kb(int e1_a, int e2_a, int rel_a, int e1_b, int e2_b, int rel_b) {float sum1 = calc_sum(e1_a, e2_a, rel_a);float sum2 = calc_sum(e1_b, e2_b, rel_b);// 不满足条件则需要更新梯度if (sum1 + margin > sum2) {res += margin + sum1 - sum2;gradient(e1_a, e2_a, rel_a, e1_b, e2_b, rel_b);}
}
// 根据相同的h返回一个假的样本t,获取三元组中相同h对应的r
int corrupt_head(int id, int h, int r) {int lef, rig, mid, ll, rr;lef = lefHead[h] - 1;rig = rigHead[h];while (lef + 1 < rig) { //则该值不止一个mid = (lef + rig) >> 1; // 除2if (trainHead[mid].r >= r) rig = mid; elselef = mid;}ll = rig; // r值对应的indexlef = lefHead[h];rig = rigHead[h] + 1;while (lef + 1 < rig) {mid = (lef + rig) >> 1;if (trainHead[mid].r <= r) lef = mid; elserig = mid;}rr = lef;int tmp = rand_max(id, entityTotal - (rr - ll + 1)); //生成一个小于entityTotal - (rr - ll + 1)的随机数if (tmp < trainHead[ll].t) return tmp; //小于初始t 直接返回if (tmp > trainHead[rr].t - rr + ll - 1) return tmp + rr - ll + 1; //lef = ll, rig = rr + 1;while (lef + 1 < rig) {mid = (lef + rig) >> 1;if (trainHead[mid].t - mid + ll - 1 < tmp)lef = mid;elserig = mid;}return tmp + lef - ll + 1;
}int corrupt_tail(int id, int t, int r) {int lef, rig, mid, ll, rr;lef = lefTail[t] - 1;rig = rigTail[t];while (lef + 1 < rig) {mid = (lef + rig) >> 1;if (trainTail[mid].r >= r) rig = mid; elselef = mid;}ll = rig;lef = lefTail[t];rig = rigTail[t] + 1;while (lef + 1 < rig) {mid = (lef + rig) >> 1;if (trainTail[mid].r <= r) lef = mid; elserig = mid;}rr = lef;int tmp = rand_max(id, entityTotal - (rr - ll + 1));if (tmp < trainTail[ll].h) return tmp;if (tmp > trainTail[rr].h - rr + ll - 1) return tmp + rr - ll + 1;lef = ll, rig = rr + 1;while (lef + 1 < rig) {mid = (lef + rig) >> 1;if (trainTail[mid].h - mid + ll - 1 < tmp)lef = mid;elserig = mid;}return tmp + lef - ll + 1;
}
// 接受线程id作为输入,调用corrupt生成正负样本,train_kb进行训练
void* transetrainMode(void *con) {int id;id = (unsigned long long)(con); //补0即可next_random[id] = rand();for (int k = transeBatch / transeThreads; k >= 0; k--) { // 一个batch训练的样本数按照线程均分int j;// 生成一个样本随机的样本idint i = rand_max(id, transeLen); // i为生成的随机数int pr = 500; //一半的概率1/2决定生成 伪head tailif (randd(id) % 1000 < pr) {// 选择正、负样本作为训练输入j = corrupt_head(id, trainList[i].h, trainList[i].r);train_kb(trainList[i].h, trainList[i].t, trainList[i].r, trainList[i].h, j, trainList[i].r);} else {j = corrupt_tail(id, trainList[i].t, trainList[i].r);train_kb(trainList[i].h, trainList[i].t, trainList[i].r, j, trainList[i].t, trainList[i].r);}norm(relationVec + dimension * trainList[i].r); // 标准化norm(entityVec + dimension * trainList[i].h);norm(entityVec + dimension * trainList[i].t);norm(entityVec + dimension * j);}pthread_exit(NULL);
}// 创建线程执行 调用transetrainMode 模型训练
void train_transe(void *con) {transeLen = tripleTotal;transeBatch = transeLen / nbatches; // 一个batch的样本大小next_random = (unsigned long long *)calloc(transeThreads, sizeof(unsigned long long)); // 根据线程数创建表示线程的数组for (int epoch = 0; epoch < transeTrainTimes; epoch++) {res = 0;// 一个epoch包含nbatches个batch,每个batch再按线程划分for (int batch = 0; batch < nbatches; batch++) {pthread_t *pt = (pthread_t *)malloc(transeThreads * sizeof(pthread_t)); // 表示线程id,可以认为unsigned long int类型for (long a = 0; a < transeThreads; a++)pthread_create(&pt[a], NULL, transetrainMode,  (void*)a); // 创建线程(指向线程标识符的指针,线程属性,运行函数的地址,运行函数的参数)for (long a = 0; a < transeThreads; a++)pthread_join(pt[a], NULL); //以阻塞的方式等待thread指定的线程结束,主线程等待直到等待的线程结束free(pt);}printf("epoch %d %f\n", epoch, res);}
}/*save result*/void out_transe() {stringstream ss;ss << dimension;string dim = ss.str();FILE* f2 = fopen((outPath + "TransE_relation2vec_" + dim + ".vec").c_str(), "w");FILE* f3 = fopen((outPath + "TransE_entity2vec_" + dim + ".vec").c_str(), "w");for (int i=0; i < relationTotal; i++) {int last = dimension * i;for (int ii = 0; ii < dimension; ii++)fprintf(f2, "%.6f\t", relationVec[last + ii]);fprintf(f2,"\n");}for (int  i = 0; i < entityTotal; i++) {int last = i * dimension;for (int ii = 0; ii < dimension; ii++)fprintf(f3, "%.6f\t", entityVec[last + ii] );fprintf(f3,"\n");}fclose(f2);fclose(f3);
}
/*Main function*/
int main() {time_t start = time(NULL);init();train_transe(NULL);out_transe();cout << time(NULL) - start << " s" << endl;return 0;
}

TransE代码实践(很详细)相关推荐

  1. 小数化分数(C++ 代码讲解很详细)

    [问题描述] 任何小数都能表示成分数的形式,对于給定的小数,编写程序其化为最简分数输出,小数包括简单小数和循环小数. [输入形式] 第一行是一个整数N,表示有多少组数据. 每组数据只有一个纯小数,也就 ...

  2. 【LiveVideoStack线上分享】FFmpeg深度学习模块架构与代码实践

    FFmpeg可谓是音视频开发中的一把瑞士军刀,其中filter提供了很多音视频特效与图像处理的功能,除了传统的FFmpeg+OpenGL/OpenCV以外,深度学习模块提供了一种新的方式.本周四晚19 ...

  3. 旋转八卦代码 java_Java的BIO和NIO很难懂?用代码实践给你看,再不懂我转行!

    本文原题"从实践角度重新理解BIO和NIO",原文由Object分享,为了更好的内容表现力,收录时有改动. 1.引言 这段时间自己在看一些Java中BIO和NIO之类的东西,也看了 ...

  4. 最简版Seq2Seq的英法机器翻译实践和详细代码解释

    Seq2Seq的英法机器翻译实践 本文的内容主要是基于英法平行语料库来实现一个简单的英法翻译模型.没有使用注意力机制和双向LSTM等技术,主要是为了掌握基本的Seq2Seq结构和TensorFlow函 ...

  5. 400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

    作者 | 无名之辈FTER 责编 | 夕颜 出品 | 程序人生(ID:coder_life) 本文翻译自Rasa官方文档,并融合了自己的理解和项目实战,同时对文档中涉及到的技术点进行了一定程度的扩展, ...

  6. python教程很详细_Python编程入门教程:从入门到高级,非常详细

    本文的资料和内容是我下载的,觉得非常有用,于是转过来大家瞧瞧: 这里给初学Python的朋友提供一些建议和指导吧.大神请无视, 俗话说:授人以鱼不如授人以渔.所以我这里只是阐述学习过程,并不会直接详细 ...

  7. 很详细的SpringBoot整合UEditor教程

    很详细的SpringBoot整合UEditor教程 2017年04月10日 20:27:21 小宝2333 阅读数:21529 版权声明:本文为博主原创文章,未经博主允许不得转载. https://b ...

  8. 不少人都想了解网络安全培训内容有哪些?这篇文章会很详细的告诉你

    现在大学生工作难找,究其原因无非是学历和技能受限造成的,大环境再怎么变化,其实大家心知肚明,只要有高学历+高技术,体面高薪的工作很多. 现在企业招人,更愿意要高技术而不是高学历的求职者,所以导致一种怪 ...

  9. 游戏陪玩系统源码,陪玩APP开发系统自动化代码实践

    android端游戏陪玩系统源码主流的Butternife ,Dragger2等很好的实现了AOP的编程理念. 在陪玩APP开发中我们利用这种思想将陪玩间很多的共有逻辑变为不可见,不需要开发人员去关注 ...

  10. usercf代码java_基于用户的协同过滤算法(UserCF)原理以及代码实践

    简介 协同过滤(collaborative filtering)是一种在推荐系统中广泛使用的技术.该技术通过分析用户或者事物之间的相似性,来预测用户可能感兴趣的内容并将此内容推荐给用户.这里的相似性可 ...

最新文章

  1. python 异常处理 实例_Python 异常处理(示例代码)
  2. MariaDB数据库介绍三、MHA(Master HA)实现主节点故障转移
  3. PHP中调用SVN命令更新网站方法(解决文件名包含中文更新失败的问题)
  4. [ACM_NYOJ_21]三个水杯(BFS广度优先搜索)
  5. ios自定义日期、时间、城市选择器
  6. java行情一年比一年差_推动Java前进? 一个定义。 一年回顾。
  7. python中的线程之semaphore信号量
  8. pytest测试框架(一)---安装及入门
  9. 一本通1623Sherlock and His Girlfriend
  10. jquery页面隐藏和展开之间切换
  11. java使用微信表情代码_iOS高仿微信表情输入功能代码分享
  12. Photoshop去除图片水印
  13. 详解从零搭建企业级 vue3 + vite2+ ts4 框架全过程
  14. 英语作文计算机国际会议开幕词,学术会议发言稿 英文(精选多篇)
  15. 【JZOJ4117】lhxsb(三角函数+凸壳+CDQ分治)
  16. flink增量读文本数据
  17. C语言文字简单加密程序的实现
  18. 第十章:项目沟通管理 - (10.1 规划沟通管理)
  19. 课程向:深度学习与人类语言处理 ——李宏毅,2020 (P17) 任务精简
  20. 第一个Android应用

热门文章

  1. wp10 手机 部署linux,【图片】02-15【吐槽】【转】win10手机端直接安装xap教程!【windowsphone吧】_百度贴吧...
  2. APU~ZPU全掌握!
  3. 搏一搏,单车变摩托!华为天才少年耗时四月将自行车强势升级为自动驾驶
  4. 中山大学delphi视频下载(51讲)
  5. vc2008程序发布指南
  6. 两种方式打开jar文件
  7. 金蝶eas怎么引出凭证_金蝶专业版如何引入引出凭证
  8. Markdown - CSND
  9. DPDK示例l3fwd性能测试
  10. 页面里引入电子表字体