diff --git a/cortex.sh b/cortex.sh new file mode 100755 index 0000000..d16f778 --- /dev/null +++ b/cortex.sh @@ -0,0 +1,468 @@ +#!/bin/bash + +# Copyright 2019 Cortex Labs, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +#################### +### FLAG PARSING ### +#################### + +flag_help=false +positional_args=() + +while [[ $# -gt 0 ]]; do + key="$1" + case $key in + -c|--config) + export CORTEX_CONFIG="$2" + shift + shift + ;; + -h|--help) + flag_help="true" + shift + ;; + *) + positional_args+=("$1") + shift + ;; + esac +done + +set -- "${positional_args[@]}" +positional_args=() +for i in "$@"; do + case $i in + -c=*|--config=*) + export CORTEX_CONFIG="${i#*=}" + shift + ;; + -h=*|--help=*) + flag_help="true" + ;; + *) + positional_args+=("$1") + shift + ;; + esac +done + +set -- "${positional_args[@]}" +if [ "$flag_help" == "true" ]; then + show_help + exit 0 +fi + +for arg in "$@"; do + if [[ "$arg" == -* ]]; then + echo "unknown flag: $arg" + show_help + exit 1 + fi +done + +##################### +### CONFIGURATION ### +##################### + +if [ "$CORTEX_CONFIG" != "" ]; then + if [ ! -f "$CORTEX_CONFIG" ]; then + echo "Cortex config file does not exist: $CORTEX_CONFIG" + exit 1 + fi + source $CORTEX_CONFIG +fi + +set -u + +export CORTEX_VERSION_STABLE=0.7.3 + +# Defaults +export AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-""}" + +if [ "$AWS_ACCESS_KEY_ID" = "" ]; then + echo -e "\nPlease set AWS_ACCESS_KEY_ID" + exit 1 +fi + +export AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-""}" + +if [ "$AWS_SECRET_ACCESS_KEY" = "" ]; then + echo -e "\nPlease set AWS_SECRET_ACCESS_KEY" + exit 1 +fi + +export CORTEX_LOG_GROUP="${CORTEX_LOG_GROUP:-cortex}" +export CORTEX_BUCKET="${CORTEX_BUCKET:-""}" +export CORTEX_REGION="${CORTEX_REGION:-us-west-2}" +export CORTEX_ZONES="${CORTEX_ZONES:-""}" + +export CORTEX_CLUSTER="${CORTEX_CLUSTER:-cortex}" +export CORTEX_NODE_TYPE="${CORTEX_NODE_TYPE:-t3.large}" +export CORTEX_NODES_MIN="${CORTEX_NODES_MIN:-2}" +export CORTEX_NODES_MAX="${CORTEX_NODES_MAX:-5}" +export CORTEX_NAMESPACE="${CORTEX_NAMESPACE:-cortex}" + +export CORTEX_IMAGE_MANAGER="${CORTEX_IMAGE_MANAGER:-cortexlabs/manager:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_FLUENTD="${CORTEX_IMAGE_FLUENTD:-cortexlabs/fluentd:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_NGINX_BACKEND="${CORTEX_IMAGE_NGINX_BACKEND:-cortexlabs/nginx-backend:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_NGINX_CONTROLLER="${CORTEX_IMAGE_NGINX_CONTROLLER:-cortexlabs/nginx-controller:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_OPERATOR="${CORTEX_IMAGE_OPERATOR:-cortexlabs/operator:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_SPARK="${CORTEX_IMAGE_SPARK:-cortexlabs/spark:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_SPARK_OPERATOR="${CORTEX_IMAGE_SPARK_OPERATOR:-cortexlabs/spark-operator:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_TF_SERVE="${CORTEX_IMAGE_TF_SERVE:-cortexlabs/tf-serve:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_TF_TRAIN="${CORTEX_IMAGE_TF_TRAIN:-cortexlabs/tf-train:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_TF_API="${CORTEX_IMAGE_TF_API:-cortexlabs/tf-api:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_PYTHON_PACKAGER="${CORTEX_IMAGE_PYTHON_PACKAGER:-cortexlabs/python-packager:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_TF_SERVE_GPU="${CORTEX_IMAGE_TF_SERVE_GPU:-cortexlabs/tf-serve-gpu:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_TF_TRAIN_GPU="${CORTEX_IMAGE_TF_TRAIN_GPU:-cortexlabs/tf-train-gpu:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_ONNX_SERVE="${CORTEX_IMAGE_ONNX_SERVE:-cortexlabs/onnx-serve:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_ONNX_SERVE_GPU="${CORTEX_IMAGE_ONNX_SERVE_GPU:-cortexlabs/onnx-serve-gpu:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_CLUSTER_AUTOSCALER="${CORTEX_IMAGE_CLUSTER_AUTOSCALER:-cortexlabs/cluster-autoscaler:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_NVIDIA="${CORTEX_IMAGE_NVIDIA:-cortexlabs/nvidia:$CORTEX_VERSION_STABLE}" +export CORTEX_IMAGE_METRICS_SERVER="${CORTEX_IMAGE_METRICS_SERVER:-cortexlabs/metrics-server:$CORTEX_VERSION_STABLE}" + +export CORTEX_ENABLE_TELEMETRY="${CORTEX_ENABLE_TELEMETRY:-""}" + +########################## +### TOP-LEVEL COMMANDS ### +########################## + +function install_eks() { + echo + docker run -it --entrypoint /root/install_eks.sh \ + -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ + -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \ + -e CORTEX_CLUSTER=$CORTEX_CLUSTER \ + -e CORTEX_REGION=$CORTEX_REGION \ + -e CORTEX_NODE_TYPE=$CORTEX_NODE_TYPE \ + -e CORTEX_NODES_MIN=$CORTEX_NODES_MIN \ + -e CORTEX_NODES_MAX=$CORTEX_NODES_MAX \ + $CORTEX_IMAGE_MANAGER +} + +function uninstall_eks() { + echo + docker run -it --entrypoint /root/uninstall_eks.sh \ + -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ + -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \ + -e CORTEX_CLUSTER=$CORTEX_CLUSTER \ + -e CORTEX_REGION=$CORTEX_REGION \ + $CORTEX_IMAGE_MANAGER +} + +function install_cortex() { + echo + docker run -it --entrypoint /root/install_cortex.sh \ + -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ + -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \ + -e CORTEX_CLUSTER=$CORTEX_CLUSTER \ + -e CORTEX_REGION=$CORTEX_REGION \ + -e CORTEX_NAMESPACE=$CORTEX_NAMESPACE \ + -e CORTEX_NODE_TYPE=$CORTEX_NODE_TYPE \ + -e CORTEX_LOG_GROUP=$CORTEX_LOG_GROUP \ + -e CORTEX_BUCKET=$CORTEX_BUCKET \ + -e CORTEX_IMAGE_FLUENTD=$CORTEX_IMAGE_FLUENTD \ + -e CORTEX_IMAGE_NGINX_BACKEND=$CORTEX_IMAGE_NGINX_BACKEND \ + -e CORTEX_IMAGE_NGINX_CONTROLLER=$CORTEX_IMAGE_NGINX_CONTROLLER \ + -e CORTEX_IMAGE_OPERATOR=$CORTEX_IMAGE_OPERATOR \ + -e CORTEX_IMAGE_SPARK=$CORTEX_IMAGE_SPARK \ + -e CORTEX_IMAGE_SPARK_OPERATOR=$CORTEX_IMAGE_SPARK_OPERATOR \ + -e CORTEX_IMAGE_TF_SERVE=$CORTEX_IMAGE_TF_SERVE \ + -e CORTEX_IMAGE_TF_TRAIN=$CORTEX_IMAGE_TF_TRAIN \ + -e CORTEX_IMAGE_TF_API=$CORTEX_IMAGE_TF_API \ + -e CORTEX_IMAGE_PYTHON_PACKAGER=$CORTEX_IMAGE_PYTHON_PACKAGER \ + -e CORTEX_IMAGE_TF_SERVE_GPU=$CORTEX_IMAGE_TF_SERVE_GPU \ + -e CORTEX_IMAGE_TF_TRAIN_GPU=$CORTEX_IMAGE_TF_TRAIN_GPU \ + -e CORTEX_IMAGE_ONNX_SERVE=$CORTEX_IMAGE_ONNX_SERVE \ + -e CORTEX_IMAGE_ONNX_SERVE_GPU=$CORTEX_IMAGE_ONNX_SERVE_GPU \ + -e CORTEX_IMAGE_CLUSTER_AUTOSCALER=$CORTEX_IMAGE_CLUSTER_AUTOSCALER \ + -e CORTEX_IMAGE_NVIDIA=$CORTEX_IMAGE_NVIDIA \ + -e CORTEX_IMAGE_METRICS_SERVER=$CORTEX_IMAGE_METRICS_SERVER \ + -e CORTEX_ENABLE_TELEMETRY=$CORTEX_ENABLE_TELEMETRY \ + $CORTEX_IMAGE_MANAGER +} + +function uninstall_operator() { + echo + docker run -it --entrypoint /root/uninstall_operator.sh \ + -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ + -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \ + -e CORTEX_CLUSTER=$CORTEX_CLUSTER \ + -e CORTEX_REGION=$CORTEX_REGION \ + -e CORTEX_NAMESPACE=$CORTEX_NAMESPACE \ + $CORTEX_IMAGE_MANAGER +} + +function info() { + echo + docker run -it --entrypoint /root/info.sh \ + -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ + -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \ + -e CORTEX_CLUSTER=$CORTEX_CLUSTER \ + -e CORTEX_REGION=$CORTEX_REGION \ + -e CORTEX_NAMESPACE=$CORTEX_NAMESPACE \ + $CORTEX_IMAGE_MANAGER +} + +################ +### CHECK OS ### +################ + +case "$OSTYPE" in + darwin*) PARSED_OS="darwin" ;; + linux*) PARSED_OS="linux" ;; + *) echo -e "\nerror: only mac and linux are supported"; exit 1 ;; +esac + +############################# +### DEPENDENCY MANAGEMENT ### +############################# + +function check_dep_curl() { + if ! command -v curl >/dev/null; then + echo -e "\nerror: please install \`curl\`" + exit 1 + fi +} + +function install_cli() { + set -e + + check_dep_curl + + echo -e "\nInstalling the Cortex CLI (/usr/local/bin/cortex) ..." + + CORTEX_SH_TMP_DIR="$HOME/.cortex-sh-tmp" + rm -rf $CORTEX_SH_TMP_DIR && mkdir -p $CORTEX_SH_TMP_DIR + curl -s -o $CORTEX_SH_TMP_DIR/cortex https://s3-us-west-2.amazonaws.com/get-cortex/$CORTEX_VERSION_STABLE/cli/$PARSED_OS/cortex + chmod +x $CORTEX_SH_TMP_DIR/cortex + + if [ $(id -u) = 0 ]; then + mv -f $CORTEX_SH_TMP_DIR/cortex /usr/local/bin/cortex + else + ask_sudo + sudo mv -f $CORTEX_SH_TMP_DIR/cortex /usr/local/bin/cortex + fi + + rm -rf $CORTEX_SH_TMP_DIR + echo "✓ Installed the Cortex CLI" + + bash_profile_path=$(get_bash_profile) + if [ ! "$bash_profile_path" = "" ]; then + if ! grep -Fxq "source <(cortex completion)" "$bash_profile_path"; then + echo + read -p "Would you like to modify your bash profile ($bash_profile_path) to enable cortex command completion and the cx alias? [Y/n] " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo -e "\nsource <(cortex completion)" >> $bash_profile_path + echo "✓ Your bash profile ($bash_profile_path) has been updated" + echo + echo "Note: \`bash_completion\` must be installed on your system for cortex command completion to function properly" + echo + echo "Command to update your current terminal session:" + echo " source $bash_profile_path" + else + echo "Your bash profile has not been modified. If you would like to modify it manually, add this line to your bash profile:" + echo " source <(cortex completion)" + echo "Note: \`bash_completion\` must be installed on your system for cortex command completion to function properly" + fi + fi + else + echo -e "\nIf your would like to enable cortex command completion and the cx alias, add this line to your bash profile:" + echo " source <(cortex completion)" + echo "Note: \`bash_completion\` must be installed on your system for cortex command completion to function properly" + fi +} + +function uninstall_cli() { + set -e + + rm -rf $HOME/.cortex + + if ! command -v cortex >/dev/null; then + echo -e "\nThe Cortex CLI is not installed" + return + fi + + if [[ ! -f /usr/local/bin/cortex ]]; then + echo -e "\nThe Cortex CLI was not found at /usr/local/bin/cortex, please uninstall it manually" + return + fi + + if [ $(id -u) = 0 ]; then + rm /usr/local/bin/cortex + else + ask_sudo + sudo rm /usr/local/bin/cortex + fi + echo -e "\n✓ Uninstalled the Cortex CLI" + + bash_profile_path=$(get_bash_profile) + if [ ! "$bash_profile_path" = "" ]; then + if grep -Fxq "source <(cortex completion)" "$bash_profile_path"; then + echo + read -p "Would you like to remove \"source <(cortex completion)\" from your bash profile ($bash_profile_path)? [Y/n] " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + sed '/^source <(cortex completion)$/d' "$bash_profile_path" > "${bash_profile_path}_cortex_modified" && mv -f "${bash_profile_path}_cortex_modified" "$bash_profile_path" + echo "✓ Your bash profile ($bash_profile_path) has been updated" + fi + fi + fi +} + +function get_bash_profile() { + if [ "$PARSED_OS" = "darwin" ]; then + if [ -f $HOME/.bash_profile ]; then + echo $HOME/.bash_profile + return + elif [ -f $HOME/.bashrc ]; then + echo $HOME/.bashrc + return + fi + else + if [ -f $HOME/.bashrc ]; then + echo $HOME/.bashrc + return + elif [ -f $HOME/.bash_profile ]; then + echo $HOME/.bash_profile + return + fi + fi + + echo "" +} + +function ask_sudo() { + if ! sudo -n true 2>/dev/null; then + echo -e "\nPlease enter your sudo password" + fi +} + +function prompt_for_telemetry() { + if [ "$CORTEX_ENABLE_TELEMETRY" != "true" ] && [ "$CORTEX_ENABLE_TELEMETRY" != "false" ]; then + while true + do + echo + read -p "Would you like to help improve Cortex by anonymously sending error reports and usage stats to the dev team? [Y/n] " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + export CORTEX_ENABLE_TELEMETRY=true + break + elif [[ $REPLY =~ ^[Nn]$ ]]; then + export CORTEX_ENABLE_TELEMETRY=false + break + fi + echo "Unexpected value, please enter \"Y\" or \"n\"" + done + fi +} + +############ +### HELP ### +############ + +function show_help() { + echo " +Usage: + ./cortex.sh command [sub-command] [flags] + +Available Commands: + install install Cortex + uninstall uninstall Cortex + update update Cortex + info information about Cortex + + install cli install the Cortex CLI + uninstall cli uninstall the Cortex CLI + +Flags: + -c, --config path to a Cortex config file + -h, --help +" +} + +###################### +### ARG PROCESSING ### +###################### + +arg1=${1:-""} +arg2=${2:-""} +arg3=${3:-""} + +if [ -z "$arg1" ]; then + show_help + exit 0 +fi + +if [ "$arg1" = "install" ]; then + if [ ! "$arg3" = "" ]; then + echo -e "\nerror: too many arguments for install command" + show_help + exit 1 + elif [ "$arg2" = "" ]; then + prompt_for_telemetry && install_eks && install_cortex && info + elif [ "$arg2" = "cli" ]; then + install_cli + elif [ "$arg2" = "" ]; then + echo -e "\nerror: missing subcommand for install" + show_help + exit 1 + else + echo -e "\nerror: invalid subcommand for install: $arg2" + show_help + exit 1 + fi +elif [ "$arg1" = "uninstall" ]; then + if [ ! "$arg3" = "" ]; then + echo -e "\nerror: too many arguments for uninstall command" + show_help + exit 1 + elif [ "$arg2" = "" ]; then + uninstall_eks + elif [ "$arg2" = "cli" ]; then + uninstall_cli + elif [ "$arg2" = "" ]; then + echo -e "\nerror: missing subcommand for uninstall" + show_help + exit 1 + else + echo -e "\nerror: invalid subcommand for uninstall: $arg2" + show_help + exit 1 + fi +elif [ "$arg1" = "update" ]; then + if [ ! "$arg2" = "" ]; then + echo -e "\nerror: too many arguments for get command" + show_help + exit 1 + else + uninstall_operator && install_cortex + fi +elif [ "$arg1" = "info" ]; then + if [ ! "$arg2" = "" ]; then + echo -e "\nerror: too many arguments for get command" + show_help + exit 1 + else + info + fi +else + echo -e "\nerror: unknown command: $arg1" + show_help + exit 1 +fi diff --git a/generator.py b/generator.py new file mode 100644 index 0000000..ac1e8b0 --- /dev/null +++ b/generator.py @@ -0,0 +1,180 @@ +import json +import os +import numpy as np +import tensorflow as tf + +import gpt2.src.model as model +from tensorflow.contrib import predictor +import gpt2.src.sample as sample +import gpt2.src.encoder as encoder +from utils import * + +pos_action_starts = ["You attack", "You tell", "You use", "You go"] + + +class StoryGenerator(): + + def __init__(self, sess, length=75, temperature=0.9, top_k=40): + + seed = None + batch_size=1 + model_path='gpt2/models/117M' + self.sess = sess + + self.enc = encoder.get_encoder(model_path) + hparams = model.default_hparams() + with open(os.path.join(model_path, 'hparams.json')) as f: + hparams.override_from_dict(json.load(f)) + + self.context = tf.placeholder(tf.int32, [batch_size, None]) + np.random.seed(seed) + tf.set_random_seed(seed) + self.output = sample.sample_sequence( + hparams=hparams, length=length, + context=self.context, + batch_size=batch_size, + ) + + saver = tf.train.Saver() + ckpt = tf.train.latest_checkpoint(model_path) + saver.restore(self.sess, ckpt) + + + def generate(self, prompt): + context_tokens = self.enc.encode(prompt) + out = self.sess.run(self.output, feed_dict={ + self.context: [context_tokens for _ in range(1)] + })[:, len(context_tokens):] + + text = self.enc.decode(out[0]) + return text + + def generate_story_block(self, prompt): + block = self.generate(prompt) + block = cut_trailing_sentence(block) + block = story_replace(block) + + return block + + def generate_action_options(self, prompt, action_starts=pos_action_starts): + + possible_actions = [] + for phrase in action_starts: + action = phrase + self.generate(prompt + phrase) + action = first_sentence(action) + possible_actions.append(action) + + return possible_actions + + def generate_action_result(self, prompt, phrase): + action = phrase + self.generate(prompt + phrase) + action_result = cut_trailing_sentence(action) + action_result = story_replace(action_result) + + action = first_sentence(action) + + + return action, action_result + + +def save_model(): + length=75 + temperature=0.9 + top_k=40 + + with tf.Session() as sess: + seed = None + batch_size=None + model_path='gpt2/models/117M' + + hparams = model.default_hparams() + with open(os.path.join(model_path, 'hparams.json')) as f: + hparams.override_from_dict(json.load(f)) + + context = tf.placeholder(tf.int32, [batch_size, None]) + np.random.seed(seed) + tf.set_random_seed(seed) + output = sample.sample_sequence( + hparams=hparams, length=length, + context=context, + batch_size=batch_size, + ) + print("***********************",type(output)) + + saver = tf.train.Saver() + ckpt = tf.train.latest_checkpoint(model_path) + saver.restore(sess, ckpt) + + tf.saved_model.simple_save(sess, "./saved2", inputs={"context": context}, outputs={"output": output}) + + +def generate_gpu_config(memory_fraction): + config = tf.ConfigProto() + config.gpu_options.allow_growth = True + config.gpu_options.per_process_gpu_memory_fraction = memory_fraction + return config + +def run_interactive(): + pass + + +def load_model(): + # Set your memory fraction equal to a value less than 1, 0.6 is a good starting point. + # If no fraction is defined, the tensorflow algorithm may run into gpu out of memory problems. + fraction = 0.6 + config = config=generate_gpu_config(fraction) + path_to_graph = "./saved" + + #tf.saved_model.loader.load( + # session, + # [tf.saved_model.tag_constants.SERVING], + # path_to_graph) + + #output = session.graph.get_tensor_by_name('output:0') + #context = session.graph.get_tensor_by_name('context:0') + model_path = 'gpt2/models/117M' + enc = encoder.get_encoder(model_path) + + predict_fn = predictor.from_saved_model(path_to_graph, config=config) + context_tokens = [enc.encode("hello")] + predictions = predict_fn({"context": context_tokens}) + output = enc.decode(predictions["output"][0]) + print(output) + + return (output, session) + +if __name__ == '__main__': + save_model() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gpt2/CONTRIBUTORS.md b/gpt2/CONTRIBUTORS.md new file mode 100644 index 0000000..eab7132 --- /dev/null +++ b/gpt2/CONTRIBUTORS.md @@ -0,0 +1,17 @@ +# Contributors (alphabetically) + +* **[madisonmay](https://github.com/madisonmay)** + + Added Dockerfiles + +* **[Margaret Mitchell et al](https://arxiv.org/abs/1810.03993)** + + Our [usage](./README.md#usage) writeup was loosely inspired by the paper + [Model Cards for Model Reporting](https://arxiv.org/abs/1810.03993) + and related conversations with some of the authors. + +* **[webproduktion01](https://github.com/webproduktion01)** + + Ported download script to python. + +**[Full code contributors list](https://github.com/openai/gpt-2/contributors).** diff --git a/gpt2/DEVELOPERS.md b/gpt2/DEVELOPERS.md new file mode 100644 index 0000000..078999b --- /dev/null +++ b/gpt2/DEVELOPERS.md @@ -0,0 +1,85 @@ +# Installation + +Git clone this repository, and `cd` into directory for remaining commands +``` +git clone https://github.com/openai/gpt-2.git && cd gpt-2 +``` + +Then, follow instructions for either native or Docker installation. + +## Native Installation + +All steps can optionally be done in a virtual environment using tools such as `virtualenv` or `conda`. + +Install tensorflow 1.12 (with GPU support, if you have a GPU and want everything to run faster) +``` +pip3 install tensorflow==1.12.0 +``` +or +``` +pip3 install tensorflow-gpu==1.12.0 +``` + +Install other python packages: +``` +pip3 install -r requirements.txt +``` + +Download the model data +``` +python3 download_model.py 117M +``` + +## Docker Installation + +Build the Dockerfile and tag the created image as `gpt-2`: +``` +docker build --tag gpt-2 -f Dockerfile.gpu . # or Dockerfile.cpu +``` + +Start an interactive bash session from the `gpt-2` docker image. + +You can opt to use the `--runtime=nvidia` flag if you have access to a NVIDIA GPU +and a valid install of [nvidia-docker 2.0](https://github.com/nvidia/nvidia-docker/wiki/Installation-(version-2.0)). +``` +docker run --runtime=nvidia -it gpt-2 bash +``` + +# Running + +| WARNING: Samples are unfiltered and may contain offensive content. | +| --- | + +Some of the examples below may include Unicode text characters. Set the environment variable: +``` +export PYTHONIOENCODING=UTF-8 +``` +to override the standard stream settings in UTF-8 mode. + +## Unconditional sample generation + +To generate unconditional samples from the small model: +``` +python3 src/generate_unconditional_samples.py | tee /tmp/samples +``` +There are various flags for controlling the samples: +``` +python3 src/generate_unconditional_samples.py --top_k 40 --temperature 0.7 | tee /tmp/samples +``` + +To check flag descriptions, use: +``` +python3 src/generate_unconditional_samples.py -- --help +``` + +## Conditional sample generation + +To give the model custom prompts, you can use: +``` +python3 src/interactive_conditional_samples.py --top_k 40 +``` + +To check flag descriptions, use: +``` +python3 src/interactive_conditional_samples.py -- --help +``` diff --git a/gpt2/LICENSE b/gpt2/LICENSE new file mode 100644 index 0000000..cb36e12 --- /dev/null +++ b/gpt2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 OpenAI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/gpt2/README.md b/gpt2/README.md new file mode 100644 index 0000000..c1be039 --- /dev/null +++ b/gpt2/README.md @@ -0,0 +1,61 @@ +# gpt-2 + +Code and samples from the paper ["Language Models are Unsupervised Multitask Learners"](https://d4mucfpksywv.cloudfront.net/better-language-models/language-models.pdf). + +For now, we have only released a smaller (117M parameter) version of GPT-2. + +See more details in our [blog post](https://blog.openai.com/better-language-models/). + +## Usage + +This repository is meant to be a starting point for researchers and engineers to experiment with GPT-2-117M. While GPT-2-117M is less proficient than GPT-2-1.5B, it is useful for a wide range of research and applications which could also apply to larger models. + +### Some caveats + +- GPT-2-117M robustness and worst case behaviors are not well-understood. As with any machine-learned model, carefully evaluate GPT-2-117M for your use case, especially if used without fine-tuning or in safety-critical applications where reliability is important. +- The dataset our GPT-2-117M was trained on contains many texts with [biases](https://twitter.com/TomerUllman/status/1101485289720242177) and factual inaccuracies, and thus GPT-2-117M is likely to be biased and inaccurate as well. +- To avoid having samples mistaken as human-written, we recommend clearly labeling samples as synthetic before wide dissemination. Our models are often incoherent or inaccurate in subtle ways, which takes more than a quick read for a human to notice. + +### Work with us + +Please [let us know](mailto:languagequestions@openai.com) if you’re doing interesting research with or working on applications of GPT-2-117M! We’re especially interested in hearing from and potentially working with those who are studying +- Potential malicious use cases and defenses against them (e.g. the detectability of synthetic text) +- The extent of problematic content (e.g. bias) being baked into the models and effective mitigations + +## Development + +See [DEVELOPERS.md](./DEVELOPERS.md) + +## Contributors + +See [CONTRIBUTORS.md](./CONTRIBUTORS.md) + +## GPT-2 samples + +| WARNING: Samples are unfiltered and may contain offensive content. | +| --- | + +While we have not yet released GPT-2 itself, you can see some samples from it in the `gpt-2-samples` folder. +We show unconditional samples with default settings (temperature 1 and no truncation), with temperature 0.7, and with truncation with top_k 40. +We show conditional samples, with contexts drawn from `WebText`'s test set, with default settings (temperature 1 and no truncation), with temperature 0.7, and with truncation with top_k 40. + +## Citation + +Please use the following bibtex entry: +``` +@article{radford2019language, + title={Language Models are Unsupervised Multitask Learners}, + author={Radford, Alec and Wu, Jeff and Child, Rewon and Luan, David and Amodei, Dario and Sutskever, Ilya}, + year={2019} +} +``` + +## Future work + +We may release code for evaluating the models on various benchmarks. + +We are still considering release of the larger models. + +## License + +[MIT](./LICENSE) diff --git a/gpt2/__init__.py b/gpt2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gpt2/download_model.py b/gpt2/download_model.py new file mode 100644 index 0000000..30ba84a --- /dev/null +++ b/gpt2/download_model.py @@ -0,0 +1,28 @@ +import os +import sys +import requests +from tqdm import tqdm + +if len(sys.argv) != 2: + print('You must enter the model name as a parameter, e.g.: download_model.py 117M') + sys.exit(1) + +model = sys.argv[1] + +subdir = os.path.join('models', model) +if not os.path.exists(subdir): + os.makedirs(subdir) +subdir = subdir.replace('\\','/') # needed for Windows + +for filename in ['checkpoint','encoder.json','hparams.json','model.ckpt.data-00000-of-00001', 'model.ckpt.index', 'model.ckpt.meta', 'vocab.bpe']: + + r = requests.get("https://storage.googleapis.com/gpt-2/" + subdir + "/" + filename, stream=True) + + with open(os.path.join(subdir, filename), 'wb') as f: + file_size = int(r.headers["content-length"]) + chunk_size = 1000 + with tqdm(ncols=100, desc="Fetching " + filename, total=file_size, unit_scale=True) as pbar: + # 1k for chunk_size, since Ethernet packet size is around 1500 bytes + for chunk in r.iter_content(chunk_size=chunk_size): + f.write(chunk) + pbar.update(chunk_size) diff --git a/gpt2/models/117M/.gitignore b/gpt2/models/117M/.gitignore new file mode 100644 index 0000000..ada5d9f --- /dev/null +++ b/gpt2/models/117M/.gitignore @@ -0,0 +1 @@ +model* \ No newline at end of file diff --git a/gpt2/models/117M/checkpoint b/gpt2/models/117M/checkpoint new file mode 100644 index 0000000..febd7d5 --- /dev/null +++ b/gpt2/models/117M/checkpoint @@ -0,0 +1,2 @@ +model_checkpoint_path: "model.ckpt" +all_model_checkpoint_paths: "model.ckpt" diff --git a/gpt2/models/117M/hparams.json b/gpt2/models/117M/hparams.json new file mode 100644 index 0000000..cc4af87 --- /dev/null +++ b/gpt2/models/117M/hparams.json @@ -0,0 +1,7 @@ +{ + "n_vocab": 50257, + "n_ctx": 1024, + "n_embd": 768, + "n_head": 12, + "n_layer": 12 +} diff --git a/gpt2/models/117M/model.ckpt.index b/gpt2/models/117M/model.ckpt.index new file mode 100644 index 0000000..24729aa Binary files /dev/null and b/gpt2/models/117M/model.ckpt.index differ diff --git a/gpt2/models/117M/model.ckpt.meta b/gpt2/models/117M/model.ckpt.meta new file mode 100644 index 0000000..2541738 Binary files /dev/null and b/gpt2/models/117M/model.ckpt.meta differ diff --git a/gpt2/src/model.py b/gpt2/src/model.py new file mode 100644 index 0000000..c66c23d --- /dev/null +++ b/gpt2/src/model.py @@ -0,0 +1,176 @@ +import numpy as np +import tensorflow as tf +from tensorflow.contrib.training import HParams + +def default_hparams(): + return HParams( + n_vocab=0, + n_ctx=1024, + n_embd=768, + n_head=12, + n_layer=12, + ) + +def shape_list(x): + """Deal with dynamic shape in tensorflow cleanly.""" + static = x.shape.as_list() + dynamic = tf.shape(x) + return [dynamic[i] if s is None else s for i, s in enumerate(static)] + +def softmax(x, axis=-1): + x = x - tf.reduce_max(x, axis=axis, keepdims=True) + ex = tf.exp(x) + return ex / tf.reduce_sum(ex, axis=axis, keepdims=True) + +def gelu(x): + return 0.5*x*(1+tf.tanh(np.sqrt(2/np.pi)*(x+0.044715*tf.pow(x, 3)))) + +def norm(x, scope, *, axis=-1, epsilon=1e-5): + """Normalize to mean = 0, std = 1, then do a diagonal affine transform.""" + with tf.variable_scope(scope): + n_state = x.shape[-1].value + g = tf.get_variable('g', [n_state], initializer=tf.constant_initializer(1)) + b = tf.get_variable('b', [n_state], initializer=tf.constant_initializer(0)) + u = tf.reduce_mean(x, axis=axis, keepdims=True) + s = tf.reduce_mean(tf.square(x-u), axis=axis, keepdims=True) + x = (x - u) * tf.rsqrt(s + epsilon) + x = x*g + b + return x + +def split_states(x, n): + """Reshape the last dimension of x into [n, x.shape[-1]/n].""" + *start, m = shape_list(x) + return tf.reshape(x, start + [n, m//n]) + +def merge_states(x): + """Smash the last two dimensions of x into a single dimension.""" + *start, a, b = shape_list(x) + return tf.reshape(x, start + [a*b]) + +def conv1d(x, scope, nf, *, w_init_stdev=0.02): + with tf.variable_scope(scope): + *start, nx = shape_list(x) + w = tf.get_variable('w', [1, nx, nf], initializer=tf.random_normal_initializer(stddev=w_init_stdev)) + b = tf.get_variable('b', [nf], initializer=tf.constant_initializer(0)) + c = tf.reshape(tf.matmul(tf.reshape(x, [-1, nx]), tf.reshape(w, [-1, nf]))+b, start+[nf]) + return c + +def attention_mask(nd, ns, *, dtype): + """1's in the lower triangle, counting from the lower right corner. + + Same as tf.matrix_band_part(tf.ones([nd, ns]), -1, ns-nd), but doesn't produce garbage on TPUs. + """ + i = tf.range(nd)[:,None] + j = tf.range(ns) + m = i >= j - ns + nd + return tf.cast(m, dtype) + + +def attn(x, scope, n_state, *, past, hparams): + assert x.shape.ndims == 3 # Should be [batch, sequence, features] + assert n_state % hparams.n_head == 0 + if past is not None: + assert past.shape.ndims == 5 # Should be [batch, 2, heads, sequence, features], where 2 is [k, v] + + def split_heads(x): + # From [batch, sequence, features] to [batch, heads, sequence, features] + return tf.transpose(split_states(x, hparams.n_head), [0, 2, 1, 3]) + + def merge_heads(x): + # Reverse of split_heads + return merge_states(tf.transpose(x, [0, 2, 1, 3])) + + def mask_attn_weights(w): + # w has shape [batch, heads, dst_sequence, src_sequence], where information flows from src to dst. + _, _, nd, ns = shape_list(w) + b = attention_mask(nd, ns, dtype=w.dtype) + b = tf.reshape(b, [1, 1, nd, ns]) + w = w*b - tf.cast(1e10, w.dtype)*(1-b) + return w + + def multihead_attn(q, k, v): + # q, k, v have shape [batch, heads, sequence, features] + w = tf.matmul(q, k, transpose_b=True) + w = w * tf.rsqrt(tf.cast(v.shape[-1].value, w.dtype)) + + w = mask_attn_weights(w) + w = softmax(w) + a = tf.matmul(w, v) + return a + + with tf.variable_scope(scope): + c = conv1d(x, 'c_attn', n_state*3) + q, k, v = map(split_heads, tf.split(c, 3, axis=2)) + present = tf.stack([k, v], axis=1) + if past is not None: + pk, pv = tf.unstack(past, axis=1) + k = tf.concat([pk, k], axis=-2) + v = tf.concat([pv, v], axis=-2) + a = multihead_attn(q, k, v) + a = merge_heads(a) + a = conv1d(a, 'c_proj', n_state) + return a, present + + +def mlp(x, scope, n_state, *, hparams): + with tf.variable_scope(scope): + nx = x.shape[-1].value + h = gelu(conv1d(x, 'c_fc', n_state)) + h2 = conv1d(h, 'c_proj', nx) + return h2 + + +def block(x, scope, *, past, hparams): + with tf.variable_scope(scope): + nx = x.shape[-1].value + a, present = attn(norm(x, 'ln_1'), 'attn', nx, past=past, hparams=hparams) + x = x + a + m = mlp(norm(x, 'ln_2'), 'mlp', nx*4, hparams=hparams) + x = x + m + return x, present + +def past_shape(*, hparams, batch_size=None, sequence=None): + return [batch_size, hparams.n_layer, 2, hparams.n_head, sequence, hparams.n_embd // hparams.n_head] + +def expand_tile(value, size): + """Add a new axis of given size.""" + value = tf.convert_to_tensor(value, name='value') + ndims = value.shape.ndims + return tf.tile(tf.expand_dims(value, axis=0), [size] + [1]*ndims) + +def positions_for(tokens, past_length): + batch_size = tf.shape(tokens)[0] + nsteps = tf.shape(tokens)[1] + return expand_tile(past_length + tf.range(nsteps), batch_size) + + +def model(hparams, X, past=None, scope='model', reuse=False): + with tf.variable_scope(scope, reuse=reuse): + results = {} + batch, sequence = shape_list(X) + + wpe = tf.get_variable('wpe', [hparams.n_ctx, hparams.n_embd], + initializer=tf.random_normal_initializer(stddev=0.01)) + wte = tf.get_variable('wte', [hparams.n_vocab, hparams.n_embd], + initializer=tf.random_normal_initializer(stddev=0.02)) + past_length = 0 if past is None else tf.shape(past)[-2] + h = tf.gather(wte, X) + tf.gather(wpe, positions_for(X, past_length)) + + # Transformer + presents = [] + pasts = tf.unstack(past, axis=1) if past is not None else [None] * hparams.n_layer + assert len(pasts) == hparams.n_layer + for layer, past in enumerate(pasts): + h, present = block(h, 'h%d' % layer, past=past, hparams=hparams) + presents.append(present) + results['present'] = tf.stack(presents, axis=1) + h = norm(h, 'ln_f') + + # Language model loss. Do tokens 1): - generate_cache() - else: - app.run(host='0.0.0.0', port=8080) + app.run(host='0.0.0.0', port=8080)