ad
当前位置: 主页 > 老鹰基金 > 正文

lf2秘籍:1美元训练BERT,教你如何薅谷歌TPU羊毛|附Colab代码

2019-07-29 18:40:20 来源:一尘投资网 点击:
ad
"\u003Cdiv\u003E\u003Cblockquote\u003E\u003Cp\u003E晓查 发自 凹非寺\u003C\u002Fp\u003E\u003Cp\u003E量子位 出品 | 公众号 QbitAI\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Cp class=\"ql-align-center\"\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cdiv class=\"pgc-img\"\u003E\u003Cimg src=\"http:\u002F\u002Fp1.pstatp.com\u002Flarge\u002Fpgc-image\u002F7e7fa9a848e043528bbbdc2b5acd77ca\" img_width=\"800\" img_height=\"450\" alt=\"1美元训练BERT,教你如何薅谷歌TPU羊毛|附Colab代码\" inline=\"0\"\u003E\u003Cp class=\"pgc-img-caption\"\u003E\u003C\u002Fp\u003E\u003C\u002Fdiv\u003E\u003Cp class=\"ql-align-center\"\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003EBERT是谷歌去年推出的NLP模型,一经推出就在各项测试中碾压竞争对手,而且BERT是开源的。只可惜训练BERT的价格实在太高,让人望而却步。\u003C\u002Fp\u003E\u003Cp\u003E之前需要用64个TPU训练4天才能完成,后来谷歌用并行计算优化了到只需一个多小时,但是需要的TPU数量陡增,达到了惊人的1024个。\u003C\u002Fp\u003E\u003Cp\u003E那么总共要多少钱呢?谷歌云TPU的使用价格是每个每小时6.5美元,训练完成训练完整个模型需要近4万美元,简直就是天价。\u003C\u002Fp\u003E\u003Cp\u003E现在,有个羊毛告诉你,在培养基上有人找到了薅谷歌羊毛的办法,只需1美元就能训练BERT,模型还能留存在你的谷歌云盘中,留作以后使用。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cstrong\u003E准备工作\u003C\u002Fstrong\u003E\u003C\u002Fp\u003E\u003Cp\u003E为了薅谷歌的羊毛,您需要一个Google云存储(Google Cloud Storage)空间。按照Google云TPU快速入门指南,创建Google云平台(Google Cloud Platform)帐户和Google云存储账户。新的谷歌云平台用户可获得300美元的免费赠送金额。\u003C\u002Fp\u003E\u003Cp class=\"ql-align-center\"\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cdiv class=\"pgc-img\"\u003E\u003Cimg src=\"http:\u002F\u002Fp3.pstatp.com\u002Flarge\u002Fpgc-image\u002Fad9854f2e94c43b691b97771651cbe19\" img_width=\"680\" img_height=\"448\" alt=\"1美元训练BERT,教你如何薅谷歌TPU羊毛|附Colab代码\" inline=\"0\"\u003E\u003Cp class=\"pgc-img-caption\"\u003E\u003C\u002Fp\u003E\u003C\u002Fdiv\u003E\u003Cp class=\"ql-align-center\"\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E在TPUv2上预训练BERT-Base模型大约需要54小时.Google Colab并非设计用于执行长时间运行的作业,它会每8小时左右中断一次训练过程。对于不间断的训练,请考虑使用付费的不间断使用TPUv2的方法。\u003C\u002Fp\u003E\u003Cp\u003E也就是说,使用Colab TPU,你可以在以1美元的价格在谷云盘上存储模型和数据,以几乎可忽略成本从头开始预训练BERT模型。\u003C\u002Fp\u003E\u003Cp\u003E以下是整个过程的代码下面的代码,可以在Colab Jupyter环境中运行。\u003C\u002Fp\u003E\u003Ch1\u003E\u003Cstrong\u003E设置训练环境\u003C\u002Fstrong\u003E\u003C\u002Fh1\u003E\u003Cp\u003E首先,安装训练模型所需的包.Jupyter允许使用直接从笔记本执行的bash命令 '!':\u003C\u002Fp\u003E\u003Cpre\u003E!pip install sentencepiece\u003Cbr\u003E!git clone https:\u002F\u002Fgithub.com\u002Fgoogle-research\u002Fbert\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E导入包并在谷歌云中授权:\u003C\u002Fp\u003E\u003Cpre\u003Eimport os\u003Cbr\u003Eimport sys\u003Cbr\u003Eimport json\u003Cbr\u003Eimport nltk\u003Cbr\u003Eimport random\u003Cbr\u003Eimport logging\u003Cbr\u003Eimport tensorflow as tf\u003Cbr\u003Eimport sentencepiece as spm\u003Cbr\u003Efrom glob import glob\u003Cbr\u003Efrom google.colab import auth, drive\u003Cbr\u003Efrom tensorflow.keras.utils import Progbar\u003Cbr\u003Esys.path.append(\"bert\")\u003Cbr\u003Efrom bert import modeling, optimization, tokenization\u003Cbr\u003Efrom bert.run_pretraining import input_fn_builder, model_fn_builder\u003Cbr\u003Eauth.authenticate_user()\u003Cbr\u003E# configure logging\u003Cbr\u003Elog = logging.getLogger('tensorflow')\u003Cbr\u003Elog.setLevel(logging.INFO)\u003Cbr\u003E# create formatter and add it to the handlers\u003Cbr\u003Eformatter = logging.Formatter('%(asctime)s : %(message)s')\u003Cbr\u003Esh = logging.StreamHandler()\u003Cbr\u003Esh.setLevel(logging.INFO)\u003Cbr\u003Esh.setFormatter(formatter)\u003Cbr\u003Elog.handlers = [sh]\u003Cbr\u003Eif 'COLAB_TPU_ADDR' in os.environ:\u003Cbr\u003E log.info(\"Using TPU runtime\")\u003Cbr\u003E USE_TPU = True\u003Cbr\u003E TPU_ADDRESS = 'grpc:\u002F\u002F' + os.environ['COLAB_TPU_ADDR']\u003Cbr\u003E with tf.Session(TPU_ADDRESS) as session:\u003Cbr\u003E log.info('TPU address is ' + TPU_ADDRESS)\u003Cbr\u003E # Upload credentials to TPU.\u003Cbr\u003E with open('\u002Fcontent\u002Fadc.json', 'r') as f:\u003Cbr\u003E auth_info = json.load(f)\u003Cbr\u003E tf.contrib.cloud.configure_gcs(session, credentials=auth_info)\u003Cbr\u003Eelse:\u003Cbr\u003E log.warning('Not connected to TPU runtime')\u003Cbr\u003E USE_TPU = False\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Ch1\u003E\u003Cstrong\u003E下载原始文本数据\u003C\u002Fstrong\u003E\u003C\u002Fh1\u003E\u003Cp\u003E接下来从网络上获取文本数据语料库。在本次实验中,我们使用OpenSubtitles数据集,该数据集包括65种语言。\u003C\u002Fp\u003E\u003Cp\u003E与更常用的文本数据集(如维基百科)不同,它不需要任何复杂的预处理,提供预格式化,一行一个句子。\u003C\u002Fp\u003E\u003Cpre\u003EAVAILABLE = {'af','ar','bg','bn','br','bs','ca','cs',\u003Cbr\u003E 'da','de','el','en','eo','es','et','eu',\u003Cbr\u003E 'fa','fi','fr','gl','he','hi','hr','hu',\u003Cbr\u003E 'hy','id','is','it','ja','ka','kk','ko',\u003Cbr\u003E 'lt','lv','mk','ml','ms','nl','no','pl',\u003Cbr\u003E 'pt','pt_br','ro','ru','si','sk','sl','sq',\u003Cbr\u003E 'sr','sv','ta','te','th','tl','tr','uk',\u003Cbr\u003E 'ur','vi','ze_en','ze_zh','zh','zh_cn',\u003Cbr\u003E 'zh_en','zh_tw','zh_zh'}\u003Cbr\u003ELANG_CODE = \"en\" #@param {type:\"string\"}\u003Cbr\u003Eassert LANG_CODE in AVAILABLE, \"Invalid language code selected\"\u003Cbr\u003E!wget http:\u002F\u002Fopus.nlpl.eu\u002Fdownload.php?f=OpenSubtitles\u002Fv2016\u002Fmono\u002FOpenSubtitles.raw.'$LANG_CODE'.gz -O dataset.txt.gz\u003Cbr\u003E!gzip -d dataset.txt.gz\u003Cbr\u003E!tail dataset.txt\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E你可以通过设置代码随意选择你需要的语言。出于演示目的,代码只默认使用整个语料库的一小部分。在实际训练模型时,请务必取消选中DEMO_MODE复选框,使用大100倍的数据集。\u003C\u002Fp\u003E\u003Cp\u003E当然,100M数据足以训练出相当不错的BERT基础模型。\u003C\u002Fp\u003E\u003Cpre\u003EDEMO_MODE = True #@param {type:\"boolean\"}\u003Cbr\u003Eif DEMO_MODE:\u003Cbr\u003E CORPUS_SIZE = 1000000\u003Cbr\u003Eelse:\u003Cbr\u003E CORPUS_SIZE = 100000000 #@param {type: \"integer\"}\u003Cbr\u003E!(head -n $CORPUS_SIZE dataset.txt) > subdataset.txt\u003Cbr\u003E!mv subdataset.txt dataset.txt\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Ch1\u003E\u003Cstrong\u003E预处理文本数据\u003C\u002Fstrong\u003E\u003C\u002Fh1\u003E\u003Cp\u003E我们下载的原始文本数据包含标点符号,大写字母和非UTF符号,我们将在继续下一步之前将其删除。在推理期间,我们将对新数据应用相同的过程。\u003C\u002Fp\u003E\u003Cp\u003E如果你需要不同的预处理方式(例如在推理期间预期会出现大写字母或标点符号),请修改以下代码以满足你的需求。\u003C\u002Fp\u003E\u003Cpre\u003Eregex_tokenizer = nltk.RegexpTokenizer(\"\\w+\")\u003Cbr\u003Edef normalize_text(text):\u003Cbr\u003E # lowercase text\u003Cbr\u003E text = str(text).lower()\u003Cbr\u003E # remove non-UTF\u003Cbr\u003E text = text.encode(\"utf-8\", \"ignore\").decode()\u003Cbr\u003E # remove punktuation symbols\u003Cbr\u003E text = \" \".join(regex_tokenizer.tokenize(text))\u003Cbr\u003E return text\u003Cbr\u003Edef count_lines(filename):\u003Cbr\u003E count = 0\u003Cbr\u003E with open(filename) as fi:\u003Cbr\u003E for line in fi:\u003Cbr\u003E count += 1\u003Cbr\u003E return count\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E现在让我们预处理整个数据集:\u003C\u002Fp\u003E\u003Cpre\u003ERAW_DATA_FPATH = \"dataset.txt\" #@param {type: \"string\"}\u003Cbr\u003EPRC_DATA_FPATH = \"proc_dataset.txt\" #@param {type: \"string\"}\u003Cbr\u003E# apply normalization to the dataset\u003Cbr\u003E# this will take a minute or two\u003Cbr\u003Etotal_lines = count_lines(RAW_DATA_FPATH)\u003Cbr\u003Ebar = Progbar(total_lines)\u003Cbr\u003Ewith open(RAW_DATA_FPATH,encoding=\"utf-8\") as fi:\u003Cbr\u003E with open(PRC_DATA_FPATH, \"w\",encoding=\"utf-8\") as fo:\u003Cbr\u003E for l in fi:\u003Cbr\u003E fo.write(normalize_text(l)+\"\\n\")\u003Cbr\u003E bar.add(1)\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Ch1\u003E\u003Cstrong\u003E构建词汇表\u003C\u002Fstrong\u003E\u003C\u002Fh1\u003E\u003Cp\u003E下一步,我们将训练模型学习一个新的词汇表,用于表示我们的数据集。\u003C\u002Fp\u003E\u003Cp\u003EBERT文件使用WordPiece分词器,在开源中不可用。我们将在单字模式下使用SentencePiece分词器。虽然它与BERT不直接兼容,但是通过一个小的处理方法,可以使它工作。\u003C\u002Fp\u003E\u003Cp\u003ESentencePiece需要相当多的运行内存,因此在Colab中的运行完整数据集会导致内核崩溃。\u003C\u002Fp\u003E\u003Cp\u003E为避免这种情况,我们将随机对数据集的一小部分进行子采样,构建词汇表。另一个选择是使用更大内存的机器来执行此步骤。\u003C\u002Fp\u003E\u003Cp\u003E此外,SentencePiece默认情况下将BOS和EOS控制符号添加到词汇表中。我们通过将其索引设置为-1来禁用它们。\u003C\u002Fp\u003E\u003Cp\u003EVOC_SIZE的典型值介于32000和128000之间。如果想要更新词汇表,并在预训练阶段结束后对模型进行微调,我们会保留NUM_PLACEHOLDERS个令牌。\u003C\u002Fp\u003E\u003Cpre\u003EMODEL_PREFIX = \"tokenizer\" #@param {type: \"string\"}\u003Cbr\u003EVOC_SIZE = 32000 #@param {type:\"integer\"}\u003Cbr\u003ESUBSAMPLE_SIZE = 12800000 #@param {type:\"integer\"}\u003Cbr\u003ENUM_PLACEHOLDERS = 256 #@param {type:\"integer\"}\u003Cbr\u003ESPM_COMMAND = ('--input={} --model_prefix={} '\u003Cbr\u003E '--vocab_size={} --input_sentence_size={} '\u003Cbr\u003E '--shuffle_input_sentence=true ' \u003Cbr\u003E '--bos_id=-1 --eos_id=-1').format(\u003Cbr\u003E PRC_DATA_FPATH, MODEL_PREFIX, \u003Cbr\u003E VOC_SIZE - NUM_PLACEHOLDERS, SUBSAMPLE_SIZE)\u003Cbr\u003Espm.SentencePieceTrainer.Train(SPM_COMMAND)\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E现在,让我们看看如何让SentencePiece在BERT模型上工作。\u003C\u002Fp\u003E\u003Cp\u003E下面是使用来自官方的预训练英语BERT基础模型的WordPiece词汇表标记的语句。\u003C\u002Fp\u003E\u003Cpre\u003E>>> wordpiece.tokenize(\"Colorless geothermal substations are generating furiously\")\u003Cbr\u003E['color',\u003Cbr\u003E '##less',\u003Cbr\u003E 'geo',\u003Cbr\u003E '##thermal',\u003Cbr\u003E 'sub',\u003Cbr\u003E '##station',\u003Cbr\u003E '##s',\u003Cbr\u003E 'are',\u003Cbr\u003E 'generating',\u003Cbr\u003E 'furiously']\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003EWordPiece标记器在“##”的单词中间预置了出现的子字。在单词开头出现的子词不变。如果子词出现在单词的开头和中间,则两个版本(带和不带” ##')都会添加到词汇表中。\u003C\u002Fp\u003E\u003Cp\u003ESentencePiece创建了两个文件:tokenizer.model和tokenizer.vocab让我们来看看它学到的词汇:\u003C\u002Fp\u003E\u003Cpre\u003Edef read_sentencepiece_vocab(filepath):\u003Cbr\u003E voc = []\u003Cbr\u003E with open(filepath, encoding='utf-8') as fi:\u003Cbr\u003E for line in fi:\u003Cbr\u003E voc.append(line.split(\"\\t\")[0])\u003Cbr\u003E # skip the first <unk> token\u003Cbr\u003E voc = voc[1:]\u003Cbr\u003E return voc\u003Cbr\u003Esnt_vocab = read_sentencepiece_vocab(\"{}.vocab\".format(MODEL_PREFIX))\u003Cbr\u003Eprint(\"Learnt vocab size: {}\".format(len(snt_vocab)))\u003Cbr\u003Eprint(\"Sample tokens: {}\".format(random.sample(snt_vocab, 10)))\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E运行结果:\u003C\u002Fp\u003E\u003Cpre\u003ELearnt vocab size: 31743 \u003Cbr\u003ESample tokens: ['▁cafe', '▁slippery', 'xious', '▁resonate', '▁terrier', '▁feat', '▁frequencies', 'ainty', '▁punning', 'modern']\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003ESentencePiece与WordPiece的运行结果完全相反从文档中可以看出:SentencePiece首先使用元符号“_”将空格转义为空格,如下所示:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E\u003Cbr\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cpre\u003EHello_World。\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E然后文本被分段为小块:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E\u003Cbr\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cpre\u003E[Hello] [_Wor] [ld] [.]\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E在空格之后出现的子词(也是大多数词开头的子词)前面加上“_”,而其他子词不变。这排除了仅出现在句子开头而不是其他地方的子词。然而,这些案件应该非常罕见。\u003C\u002Fp\u003E\u003Cp\u003E因此,为了获得类似于WordPiece的词汇表,我们需要执行一个简单的转换,从包含它的标记中删除“_”,并将“##”添加到不包含它的标记中。\u003C\u002Fp\u003E\u003Cp\u003E我们还添加了一些BERT架构所需的特殊控制符号。按照惯例,我们把它们放在词汇的开头。\u003C\u002Fp\u003E\u003Cp\u003E另外,我们在词汇表中添加了一些占位符标记。\u003C\u002Fp\u003E\u003Cp\u003E如果你希望使用新的用于特定任务的令牌来更新预先训练的模型,那么这些方法是很有用的。\u003C\u002Fp\u003E\u003Cp\u003E在这种情况下,占位符标记被替换为新的令牌,重新生成预训练数据,并且对新数据进行微调。\u003C\u002Fp\u003E\u003Cpre\u003Edef parse_sentencepiece_token(token):\u003Cbr\u003E if token.startswith(\"▁\"):\u003Cbr\u003E return token[1:]\u003Cbr\u003E else:\u003Cbr\u003E return \"##\" + token\u003Cbr\u003Ebert_vocab = list(map(parse_sentencepiece_token, snt_vocab))\u003Cbr\u003Ectrl_symbols = [\"[PAD]\",\"[UNK]\",\"[CLS]\",\"[SEP]\",\"[MASK]\"]\u003Cbr\u003Ebert_vocab = ctrl_symbols + bert_vocab\u003Cbr\u003Ebert_vocab += [\"[UNUSED_{}]\".format(i) for i in range(VOC_SIZE - len(bert_vocab))]\u003Cbr\u003Eprint(len(bert_vocab))\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E最后,我们将获得的词汇表写入文件。\u003C\u002Fp\u003E\u003Cpre\u003EVOC_FNAME = \"vocab.txt\" #@param {type:\"string\"}\u003Cbr\u003Ewith open(VOC_FNAME, \"w\") as fo:\u003Cbr\u003E for token in bert_vocab:\u003Cbr\u003E fo.write(token+\"\\n\")\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E现在,让我们看看新词汇在实践中是如何运作的:\u003C\u002Fp\u003E\u003Cpre\u003E>>> testcase = \"Colorless geothermal substations are generating furiously\"\u003Cbr\u003E>>> bert_tokenizer = tokenization.FullTokenizer(VOC_FNAME)\u003Cbr\u003E>>> bert_tokenizer.tokenize(testcase)\u003Cbr\u003E['color', \u003Cbr\u003E '##less', \u003Cbr\u003E 'geo', \u003Cbr\u003E '##ther', \u003Cbr\u003E '##mal', \u003Cbr\u003E 'sub', \u003Cbr\u003E '##station', \u003Cbr\u003E '##s', \u003Cbr\u003E 'are', \u003Cbr\u003E 'generat', \u003Cbr\u003E '##ing', \u003Cbr\u003E 'furious', \u003Cbr\u003E '##ly']\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Ch1\u003E\u003Cstrong\u003E创建分片预训练数据(生成预训练数据)\u003C\u002Fstrong\u003E\u003C\u002Fh1\u003E\u003Cp\u003E通过手头的词汇表,我们可以为BERT模型生成预训练数据。\u003C\u002Fp\u003E\u003Cp\u003E由于我们的数据集可能非常大,我们将其拆分为碎片:\u003C\u002Fp\u003E\u003Cpre\u003Emkdir .\u002Fshards\u003Cbr\u003Esplit -a 4 -l 256000 -d $PRC_DATA_FPATH .\u002Fshards\u002Fshard_\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E现在,对于每个部分,我们需要从BERT仓库调用create_pretraining_data.py脚本,需要使用xargs的命令。\u003C\u002Fp\u003E\u003Cp\u003E在开始生成之前,我们需要设置一些参数传递给脚本。你可以从自述文件中找到有关它们含义的更多信息。\u003C\u002Fp\u003E\u003Cpre\u003EMAX_SEQ_LENGTH = 128 #@param {type:\"integer\"}\u003Cbr\u003EMASKED_LM_PROB = 0.15 #@param\u003Cbr\u003EMAX_PREDICTIONS = 20 #@param {type:\"integer\"}\u003Cbr\u003EDO_LOWER_CASE = True #@param {type:\"boolean\"}\u003Cbr\u003EPRETRAINING_DIR = \"pretraining_data\" #@param {type:\"string\"}\u003Cbr\u003E# controls how many parallel processes xargs can create\u003Cbr\u003EPROCESSES = 2 #@param {type:\"integer\"}\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E运行此操作可能需要相当长的时间,具体取决于数据集的大小。\u003C\u002Fp\u003E\u003Cpre\u003EXARGS_CMD = (\"ls .\u002Fshards\u002F | \"\u003Cbr\u003E \"xargs -n 1 -P {} -I{} \"\u003Cbr\u003E \"python3 bert\u002Fcreate_pretraining_data.py \"\u003Cbr\u003E \"--input_file=.\u002Fshards\u002F{} \"\u003Cbr\u003E \"--output_file={}\u002F{}.tfrecord \"\u003Cbr\u003E \"--vocab_file={} \"\u003Cbr\u003E \"--do_lower_case={} \"\u003Cbr\u003E \"--max_predictions_per_seq={} \"\u003Cbr\u003E \"--max_seq_length={} \"\u003Cbr\u003E \"--masked_lm_prob={} \"\u003Cbr\u003E \"--random_seed=34 \"\u003Cbr\u003E \"--dupe_factor=5\")\u003Cbr\u003EXARGS_CMD = XARGS_CMD.format(PROCESSES, '{}', '{}', PRETRAINING_DIR, '{}', \u003Cbr\u003E VOC_FNAME, DO_LOWER_CASE, \u003Cbr\u003E MAX_PREDICTIONS, MAX_SEQ_LENGTH, MASKED_LM_PROB)\u003Cbr\u003Etf.gfile.MkDir(PRETRAINING_DIR)\u003Cbr\u003E!$XARGS_CMD\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Ch1\u003E\u003Cstrong\u003E为数据和模型设置GCS存储,将数据和模型存储到云端\u003C\u002Fstrong\u003E\u003C\u002Fh1\u003E\u003Cp\u003E为了保留来之不易的训练模型,我们会将其保留在谷歌云存储中。\u003C\u002Fp\u003E\u003Cp\u003E在谷歌云存储中创建两个目录,一个用于数据,一个用于模型。在模型目录中,我们将放置模型词汇表和配置文件。\u003C\u002Fp\u003E\u003Cp\u003E在继续操作之前,请配置BUCKET_NAME变量,否则将无法训练模型。\u003C\u002Fp\u003E\u003Cpre\u003EBUCKET_NAME = \"bert_resourses\" #@param {type:\"string\"}\u003Cbr\u003EMODEL_DIR = \"bert_model\" #@param {type:\"string\"}\u003Cbr\u003Etf.gfile.MkDir(MODEL_DIR)\u003Cbr\u003Eif not BUCKET_NAME:\u003Cbr\u003E log.warning(\"WARNING: BUCKET_NAME is not set. \"\u003Cbr\u003E \"You will not be able to train the model.\")\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E下面是BERT基的超参数配置示例:\u003C\u002Fp\u003E\u003Cpre\u003E# use this for BERT-base\u003Cbr\u003Ebert_base_config = {\u003Cbr\u003E \"attention_probs_dropout_prob\": 0.1, \u003Cbr\u003E \"directionality\": \"bidi\", \u003Cbr\u003E \"hidden_act\": \"gelu\", \u003Cbr\u003E \"hidden_dropout_prob\": 0.1, \u003Cbr\u003E \"hidden_size\": 768, \u003Cbr\u003E \"initializer_range\": 0.02, \u003Cbr\u003E \"intermediate_size\": 3072, \u003Cbr\u003E \"max_position_embeddings\": 512, \u003Cbr\u003E \"num_attention_heads\": 12, \u003Cbr\u003E \"num_hidden_layers\": 12, \u003Cbr\u003E \"pooler_fc_size\": 768, \u003Cbr\u003E \"pooler_num_attention_heads\": 12, \u003Cbr\u003E \"pooler_num_fc_layers\": 3, \u003Cbr\u003E \"pooler_size_per_head\": 128, \u003Cbr\u003E \"pooler_type\": \"first_token_transform\", \u003Cbr\u003E \"type_vocab_size\": 2, \u003Cbr\u003E \"vocab_size\": VOC_SIZE\u003Cbr\u003E}\u003Cbr\u003Ewith open(\"{}\u002Fbert_config.json\".format(MODEL_DIR), \"w\") as fo:\u003Cbr\u003E json.dump(bert_base_config, fo, indent=2)\u003Cbr\u003Ewith open(\"{}\u002F{}\".format(MODEL_DIR, VOC_FNAME), \"w\") as fo:\u003Cbr\u003E for token in bert_vocab:\u003Cbr\u003E fo.write(token+\"\\n\")\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E现在,我们已准备好将模型和数据存储到谷歌云当中:\u003C\u002Fp\u003E\u003Cpre\u003Eif BUCKET_NAME:\u003Cbr\u003E !gsutil -m cp -r $MODEL_DIR $PRETRAINING_DIR gs:\u002F\u002F$BUCKET_NAME\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Ch1\u003E\u003Cstrong\u003E在云TPU上训练模型\u003C\u002Fstrong\u003E\u003C\u002Fh1\u003E\u003Cp\u003E注意,之前步骤中的某些参数在此处不用改变。请确保在整个实验中设置的参数完全相同。\u003C\u002Fp\u003E\u003Cpre\u003EBUCKET_NAME = \"bert_resourses\" #@param {type:\"string\"}\u003Cbr\u003EMODEL_DIR = \"bert_model\" #@param {type:\"string\"}\u003Cbr\u003EPRETRAINING_DIR = \"pretraining_data\" #@param {type:\"string\"}\u003Cbr\u003EVOC_FNAME = \"vocab.txt\" #@param {type:\"string\"}\u003Cbr\u003E# Input data pipeline config\u003Cbr\u003ETRAIN_BATCH_SIZE = 128 #@param {type:\"integer\"}\u003Cbr\u003EMAX_PREDICTIONS = 20 #@param {type:\"integer\"}\u003Cbr\u003EMAX_SEQ_LENGTH = 128 #@param {type:\"integer\"}\u003Cbr\u003EMASKED_LM_PROB = 0.15 #@param\u003Cbr\u003E# Training procedure config\u003Cbr\u003EEVAL_BATCH_SIZE = 64\u003Cbr\u003ELEARNING_RATE = 2e-5\u003Cbr\u003ETRAIN_STEPS = 1000000 #@param {type:\"integer\"}\u003Cbr\u003ESAVE_CHECKPOINTS_STEPS = 2500 #@param {type:\"integer\"}\u003Cbr\u003ENUM_TPU_CORES = 8\u003Cbr\u003Eif BUCKET_NAME:\u003Cbr\u003E BUCKET_PATH = \"gs:\u002F\u002F{}\".format(BUCKET_NAME)\u003Cbr\u003Eelse:\u003Cbr\u003E BUCKET_PATH = \".\"\u003Cbr\u003EBERT_GCS_DIR = \"{}\u002F{}\".format(BUCKET_PATH, MODEL_DIR)\u003Cbr\u003EDATA_GCS_DIR = \"{}\u002F{}\".format(BUCKET_PATH, PRETRAINING_DIR)\u003Cbr\u003EVOCAB_FILE = os.path.join(BERT_GCS_DIR, VOC_FNAME)\u003Cbr\u003ECONFIG_FILE = os.path.join(BERT_GCS_DIR, \"bert_config.json\")\u003Cbr\u003EINIT_CHECKPOINT = tf.train.latest_checkpoint(BERT_GCS_DIR)\u003Cbr\u003Ebert_config = modeling.BertConfig.from_json_file(CONFIG_FILE)\u003Cbr\u003Einput_files = tf.gfile.Glob(os.path.join(DATA_GCS_DIR,'*tfrecord'))\u003Cbr\u003Elog.info(\"Using checkpoint: {}\".format(INIT_CHECKPOINT))\u003Cbr\u003Elog.info(\"Using {} data shards\".format(len(input_files)))\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E准备训练运行配置,建立评估器和输入函数,启动BERT!\u003C\u002Fp\u003E\u003Cpre\u003Emodel_fn = model_fn_builder(\u003Cbr\u003E bert_config=bert_config,\u003Cbr\u003E init_checkpoint=INIT_CHECKPOINT,\u003Cbr\u003E learning_rate=LEARNING_RATE,\u003Cbr\u003E num_train_steps=TRAIN_STEPS,\u003Cbr\u003E num_warmup_steps=10,\u003Cbr\u003E use_tpu=USE_TPU,\u003Cbr\u003E use_one_hot_embeddings=True)\u003Cbr\u003Etpu_cluster_resolver = tf.contrib.cluster_resolver.TPUClusterResolver(TPU_ADDRESS)\u003Cbr\u003Erun_config = tf.contrib.tpu.RunConfig(\u003Cbr\u003E cluster=tpu_cluster_resolver,\u003Cbr\u003E model_dir=BERT_GCS_DIR,\u003Cbr\u003E save_checkpoints_steps=SAVE_CHECKPOINTS_STEPS,\u003Cbr\u003E tpu_config=tf.contrib.tpu.TPUConfig(\u003Cbr\u003E iterations_per_loop=SAVE_CHECKPOINTS_STEPS,\u003Cbr\u003E num_shards=NUM_TPU_CORES,\u003Cbr\u003E per_host_input_for_training=tf.contrib.tpu.InputPipelineConfig.PER_HOST_V2))\u003Cbr\u003Eestimator = tf.contrib.tpu.TPUEstimator(\u003Cbr\u003E use_tpu=USE_TPU,\u003Cbr\u003E model_fn=model_fn,\u003Cbr\u003E config=run_config,\u003Cbr\u003E train_batch_size=TRAIN_BATCH_SIZE,\u003Cbr\u003E eval_batch_size=EVAL_BATCH_SIZE)\u003Cbr\u003Etrain_input_fn = input_fn_builder(\u003Cbr\u003E input_files=input_files,\u003Cbr\u003E max_seq_length=MAX_SEQ_LENGTH,\u003Cbr\u003E max_predictions_per_seq=MAX_PREDICTIONS,\u003Cbr\u003E is_training=True)\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E执行!\u003C\u002Fp\u003E\u003Cpre\u003Eestimator.train(input_fn=train_input_fn, max_steps=TRAIN_STEPS)\u003Cbr\u003E\u003C\u002Fpre\u003E\u003Cp\u003E最后,使用默认参数训练模型需要100万步,约54小时的运行时间。如果内核由于某种原因重新启动,可以从断点处继续训练。\u003C\u002Fp\u003E\u003Cp\u003E以上就是是在云TPU上从头开始预训练BERT的指南。\u003C\u002Fp\u003E\u003Ch1\u003E\u003Cstrong\u003E下一步\u003C\u002Fstrong\u003E\u003C\u002Fh1\u003E\u003Cp\u003E好的,我们已经训练好了模型,接下来可以做什么?\u003C\u002Fp\u003E\u003Cp\u003E如图1所示,使用预训练的模型作为通用的自然语言理解模块;\u003C\u002Fp\u003E\u003Cp\u003E2,针对某些特定的分类任务微调模型;\u003C\u002Fp\u003E\u003Cp\u003E3,使用BERT作为构建块,去创建另一个深度学习模型。\u003C\u002Fp\u003E\u003Ch1\u003E\u003Cstrong\u003E传送门\u003C\u002Fstrong\u003E\u003C\u002Fh1\u003E\u003Cp\u003E原文地址:\u003C\u002Fp\u003E\u003Cp\u003Ehttps :\u002F\u002Ftowardsdatascience.com\u002Fpre-training-bert-from-scratch-with-cloud-tpu-6e2f71028379\u003C\u002Fp\u003E\u003Cp\u003EColab代码:\u003C\u002Fp\u003E\u003Cp\u003Ehttps :\u002F\u002Fcolab.research.google.com\u002Fdrive\u002F1nVn6AFpQSzXBt8_ywfx6XR8ZfQXlKGAz\u003C\u002Fp\u003E\u003Cp\u003E— 完 —\u003C\u002Fp\u003E\u003Cp\u003E诚挚招聘\u003C\u002Fp\u003E\u003Cp\u003E量子位正在招募编辑\u002F记者,工作地点在北京中关村。期待有才气、有热情的同学加入我们!相关细节,请在量子位公众号(QbitAI)对话界面,回复“招聘”两个字。\u003C\u002Fp\u003E\u003Cp\u003E量子位 QbitAI · 头条号签约作者\u003C\u002Fp\u003E\u003Cp\u003Eվ'ᴗ' ի 追踪AI技术和产品新动态\u003C\u002Fp\u003E\u003C\u002Fdiv\u003E"'.slice(6, -6),
------分隔线----------------------------
ad
推广信息
ad
ad
头条新闻
ad
图片新闻
新闻排行榜
ad
文章推荐
ad