mirror of
				https://github.com/KoboldAI/KoboldAI-Client.git
				synced 2025-06-05 21:59:24 +02:00 
			
		
		
		
	Merge pull request #61 from VE-FORBRYDERNE/xmap
Use original TPU backend when possible
This commit is contained in:
		
							
								
								
									
										148
									
								
								aiserver.py
									
									
									
									
									
								
							
							
						
						
									
										148
									
								
								aiserver.py
									
									
									
									
									
								
							@@ -157,6 +157,7 @@ class vars:
 | 
			
		||||
    spmeta      = None   # Metadata of current soft prompt, or None if not using a soft prompt
 | 
			
		||||
    sp          = None   # Current soft prompt tensor (as a NumPy array)
 | 
			
		||||
    sp_length   = 0      # Length of current soft prompt in tokens, or 0 if not using a soft prompt
 | 
			
		||||
    has_genmod  = False  # Whether or not at least one loaded Lua userscript has a generation modifier
 | 
			
		||||
    svowname    = ""     # Filename that was flagged for overwrite confirm
 | 
			
		||||
    saveow      = False  # Whether or not overwrite confirm has been displayed
 | 
			
		||||
    genseqs     = []     # Temporary storage for generated sequences
 | 
			
		||||
@@ -184,6 +185,7 @@ class vars:
 | 
			
		||||
    remote      = False
 | 
			
		||||
    nopromptgen = False
 | 
			
		||||
    rngpersist  = False
 | 
			
		||||
    nogenmod    = False
 | 
			
		||||
 | 
			
		||||
#==================================================================#
 | 
			
		||||
# Function to get model selection at startup
 | 
			
		||||
@@ -1070,19 +1072,6 @@ else:
 | 
			
		||||
        vars.allowsp = True
 | 
			
		||||
        vars.modeldim = int(tpu_mtj_backend.params["d_model"])
 | 
			
		||||
        tokenizer = tpu_mtj_backend.tokenizer
 | 
			
		||||
        soft_tokens = tpumtjgetsofttokens()
 | 
			
		||||
        threading.Thread(  # Compile backend code in background
 | 
			
		||||
            target=tpu_mtj_backend.infer,
 | 
			
		||||
            args=(np.tile(np.uint32((23403, 727, 20185)), (vars.numseqs, 1)),),
 | 
			
		||||
            kwargs={
 | 
			
		||||
                "soft_embeddings": vars.sp,
 | 
			
		||||
                "soft_tokens": soft_tokens,
 | 
			
		||||
                "use_callback": False,
 | 
			
		||||
                "gen_len": 1,
 | 
			
		||||
                "numseqs": vars.numseqs,
 | 
			
		||||
                "excluded_world_info": list(set() for _ in range(vars.numseqs)),
 | 
			
		||||
            },
 | 
			
		||||
        ).start()
 | 
			
		||||
 | 
			
		||||
# Set up Flask routes
 | 
			
		||||
@app.route('/')
 | 
			
		||||
@@ -1190,13 +1179,18 @@ def load_lua_scripts():
 | 
			
		||||
            modulenames.append(lst[i]["modulename"])
 | 
			
		||||
            descriptions.append(lst[i]["description"])
 | 
			
		||||
 | 
			
		||||
    vars.has_genmod = False
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        vars.lua_koboldbridge.obliterate_multiverse()
 | 
			
		||||
        tpool.execute(vars.lua_koboldbridge.load_corescript, vars.corescript)
 | 
			
		||||
        tpool.execute(vars.lua_koboldbridge.load_userscripts, filenames, modulenames, descriptions)
 | 
			
		||||
        vars.has_genmod = tpool.execute(vars.lua_koboldbridge.load_userscripts, filenames, modulenames, descriptions)
 | 
			
		||||
        vars.lua_running = True
 | 
			
		||||
    except lupa.LuaError as e:
 | 
			
		||||
        vars.lua_koboldbridge.obliterate_multiverse()
 | 
			
		||||
        try:
 | 
			
		||||
            vars.lua_koboldbridge.obliterate_multiverse()
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
        vars.lua_running = False
 | 
			
		||||
        if(vars.serverstarted):
 | 
			
		||||
            emit('from_server', {'cmd': 'errmsg', 'data': 'Lua script error, please check console.'}, broadcast=True)
 | 
			
		||||
@@ -2043,6 +2037,10 @@ def get_message(msg):
 | 
			
		||||
        vars.rngpersist = msg['data']
 | 
			
		||||
        settingschanged()
 | 
			
		||||
        refresh_settings()
 | 
			
		||||
    elif(msg['cmd'] == 'setnogenmod'):
 | 
			
		||||
        vars.nogenmod = msg['data']
 | 
			
		||||
        settingschanged()
 | 
			
		||||
        refresh_settings()
 | 
			
		||||
    elif(not vars.remote and msg['cmd'] == 'importwi'):
 | 
			
		||||
        wiimportrequest()
 | 
			
		||||
 | 
			
		||||
@@ -2113,6 +2111,8 @@ def savesettings():
 | 
			
		||||
    js["dynamicscan"] = vars.dynamicscan
 | 
			
		||||
    js["nopromptgen"] = vars.nopromptgen
 | 
			
		||||
    js["rngpersist"]  = vars.rngpersist
 | 
			
		||||
    js["nogenmod"]    = vars.nogenmod
 | 
			
		||||
 | 
			
		||||
    js["antemplate"]  = vars.setauthornotetemplate
 | 
			
		||||
 | 
			
		||||
    js["userscripts"] = vars.userscripts
 | 
			
		||||
@@ -2178,6 +2178,8 @@ def loadsettings():
 | 
			
		||||
            vars.nopromptgen = js["nopromptgen"]
 | 
			
		||||
        if("rngpersist" in js):
 | 
			
		||||
            vars.rngpersist = js["rngpersist"]
 | 
			
		||||
        if("nogenmod" in js):
 | 
			
		||||
            vars.nogenmod = js["nogenmod"]
 | 
			
		||||
 | 
			
		||||
        if("antemplate" in js):
 | 
			
		||||
            vars.setauthornotetemplate = js["antemplate"]
 | 
			
		||||
@@ -2960,16 +2962,62 @@ def tpumtjgenerate(txt, minimum, maximum, found_entries=None):
 | 
			
		||||
 | 
			
		||||
    # Submit input text to generator
 | 
			
		||||
    try:
 | 
			
		||||
        context = np.tile(np.uint32(txt), (vars.numseqs, 1))
 | 
			
		||||
        soft_tokens = tpumtjgetsofttokens()
 | 
			
		||||
 | 
			
		||||
        global past
 | 
			
		||||
        past = np.empty((vars.numseqs, 0), dtype=np.uint32)
 | 
			
		||||
 | 
			
		||||
        while(True):
 | 
			
		||||
            genout, n_generated, regeneration_required, halt = tpool.execute(
 | 
			
		||||
                tpu_mtj_backend.infer,
 | 
			
		||||
                context,
 | 
			
		||||
        if(vars.dynamicscan or (not vars.nogenmod and vars.has_genmod)):
 | 
			
		||||
 | 
			
		||||
            context = np.tile(np.uint32(txt), (vars.numseqs, 1))
 | 
			
		||||
            past = np.empty((vars.numseqs, 0), dtype=np.uint32)
 | 
			
		||||
 | 
			
		||||
            while(True):
 | 
			
		||||
                genout, n_generated, regeneration_required, halt = tpool.execute(
 | 
			
		||||
                    tpu_mtj_backend.infer_dynamic,
 | 
			
		||||
                    context,
 | 
			
		||||
                    gen_len = maximum-minimum+1,
 | 
			
		||||
                    temp=vars.temp,
 | 
			
		||||
                    top_p=vars.top_p,
 | 
			
		||||
                    top_k=vars.top_k,
 | 
			
		||||
                    tfs=vars.tfs,
 | 
			
		||||
                    numseqs=vars.numseqs,
 | 
			
		||||
                    repetition_penalty=vars.rep_pen,
 | 
			
		||||
                    soft_embeddings=vars.sp,
 | 
			
		||||
                    soft_tokens=soft_tokens,
 | 
			
		||||
                    excluded_world_info=found_entries,
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                past = np.pad(past, ((0, 0), (0, n_generated)))
 | 
			
		||||
                for r in range(vars.numseqs):
 | 
			
		||||
                    for c in range(vars.lua_koboldbridge.generated_cols):
 | 
			
		||||
                        assert vars.lua_koboldbridge.generated[r+1][c+1] is not None
 | 
			
		||||
                        past[r, c] = vars.lua_koboldbridge.generated[r+1][c+1]
 | 
			
		||||
 | 
			
		||||
                if(halt or not regeneration_required):
 | 
			
		||||
                    break
 | 
			
		||||
                print("(regeneration triggered)")
 | 
			
		||||
 | 
			
		||||
                encoded = []
 | 
			
		||||
                for i in range(vars.numseqs):
 | 
			
		||||
                    txt = tokenizer.decode(past[i])
 | 
			
		||||
                    winfo, mem, anotetxt, _found_entries = calcsubmitbudgetheader(txt, force_use_txt=True)
 | 
			
		||||
                    found_entries[i].update(_found_entries)
 | 
			
		||||
                    txt, _, _ = calcsubmitbudget(len(vars._actions), winfo, mem, anotetxt, vars._actions, submission=txt)
 | 
			
		||||
                    encoded.append(np.array(txt, dtype=np.uint32))
 | 
			
		||||
                max_length = len(max(encoded, key=len))
 | 
			
		||||
                encoded = np.stack(tuple(np.pad(e, (max_length - len(e), 0), constant_values=tpu_mtj_backend.pad_token_id) for e in encoded))
 | 
			
		||||
                context = np.concatenate(
 | 
			
		||||
                    (
 | 
			
		||||
                        encoded,
 | 
			
		||||
                        past,
 | 
			
		||||
                    ),
 | 
			
		||||
                    axis=-1,
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            genout = tpool.execute(
 | 
			
		||||
                tpu_mtj_backend.infer_static,
 | 
			
		||||
                np.uint32(txt),
 | 
			
		||||
                gen_len = maximum-minimum+1,
 | 
			
		||||
                temp=vars.temp,
 | 
			
		||||
                top_p=vars.top_p,
 | 
			
		||||
@@ -2979,35 +3027,10 @@ def tpumtjgenerate(txt, minimum, maximum, found_entries=None):
 | 
			
		||||
                repetition_penalty=vars.rep_pen,
 | 
			
		||||
                soft_embeddings=vars.sp,
 | 
			
		||||
                soft_tokens=soft_tokens,
 | 
			
		||||
                excluded_world_info=found_entries,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            past = np.pad(past, ((0, 0), (0, n_generated)))
 | 
			
		||||
            for r in range(vars.numseqs):
 | 
			
		||||
                for c in range(vars.lua_koboldbridge.generated_cols):
 | 
			
		||||
                    assert vars.lua_koboldbridge.generated[r+1][c+1] is not None
 | 
			
		||||
                    past[r, c] = vars.lua_koboldbridge.generated[r+1][c+1]
 | 
			
		||||
 | 
			
		||||
            if(halt or not regeneration_required):
 | 
			
		||||
                break
 | 
			
		||||
            print("(regeneration triggered)")
 | 
			
		||||
 | 
			
		||||
            encoded = []
 | 
			
		||||
            past = genout
 | 
			
		||||
            for i in range(vars.numseqs):
 | 
			
		||||
                txt = tokenizer.decode(past[i])
 | 
			
		||||
                winfo, mem, anotetxt, _found_entries = calcsubmitbudgetheader(txt, force_use_txt=True)
 | 
			
		||||
                found_entries[i].update(_found_entries)
 | 
			
		||||
                txt, _, _ = calcsubmitbudget(len(vars._actions), winfo, mem, anotetxt, vars._actions, submission=txt)
 | 
			
		||||
                encoded.append(np.array(txt, dtype=np.uint32))
 | 
			
		||||
            max_length = len(max(encoded, key=len))
 | 
			
		||||
            encoded = np.stack(tuple(np.pad(e, (max_length - len(e), 0), constant_values=tpu_mtj_backend.pad_token_id) for e in encoded))
 | 
			
		||||
            context = np.concatenate(
 | 
			
		||||
                (
 | 
			
		||||
                    encoded,
 | 
			
		||||
                    past,
 | 
			
		||||
                ),
 | 
			
		||||
                axis=-1,
 | 
			
		||||
            )
 | 
			
		||||
                vars.lua_koboldbridge.generated[i+1] = vars.lua_state.table(*genout[i].tolist())
 | 
			
		||||
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        if(issubclass(type(e), lupa.LuaError)):
 | 
			
		||||
@@ -3189,6 +3212,7 @@ def refresh_settings():
 | 
			
		||||
    emit('from_server', {'cmd': 'updatedynamicscan', 'data': vars.dynamicscan}, broadcast=True)
 | 
			
		||||
    emit('from_server', {'cmd': 'updatenopromptgen', 'data': vars.nopromptgen}, broadcast=True)
 | 
			
		||||
    emit('from_server', {'cmd': 'updaterngpersist', 'data': vars.rngpersist}, broadcast=True)
 | 
			
		||||
    emit('from_server', {'cmd': 'updatenogenmod', 'data': vars.nogenmod}, broadcast=True)
 | 
			
		||||
    
 | 
			
		||||
    emit('from_server', {'cmd': 'updatefrmttriminc', 'data': vars.formatoptns["frmttriminc"]}, broadcast=True)
 | 
			
		||||
    emit('from_server', {'cmd': 'updatefrmtrmblln', 'data': vars.formatoptns["frmtrmblln"]}, broadcast=True)
 | 
			
		||||
@@ -4442,6 +4466,34 @@ def randomGameRequest(topic, memory=""):
 | 
			
		||||
loadmodelsettings()
 | 
			
		||||
loadsettings()
 | 
			
		||||
 | 
			
		||||
# Precompile TPU backend if required
 | 
			
		||||
if(vars.model in ("TPUMeshTransformerGPTJ",)):
 | 
			
		||||
    soft_tokens = tpumtjgetsofttokens()
 | 
			
		||||
    if(vars.dynamicscan or (not vars.nogenmod and vars.has_genmod)):
 | 
			
		||||
        threading.Thread(
 | 
			
		||||
            target=tpu_mtj_backend.infer_dynamic,
 | 
			
		||||
            args=(np.tile(np.uint32((23403, 727, 20185)), (vars.numseqs, 1)),),
 | 
			
		||||
            kwargs={
 | 
			
		||||
                "soft_embeddings": vars.sp,
 | 
			
		||||
                "soft_tokens": soft_tokens,
 | 
			
		||||
                "gen_len": 1,
 | 
			
		||||
                "use_callback": False,
 | 
			
		||||
                "numseqs": vars.numseqs,
 | 
			
		||||
                "excluded_world_info": list(set() for _ in range(vars.numseqs)),
 | 
			
		||||
            },
 | 
			
		||||
        ).start()
 | 
			
		||||
    else:
 | 
			
		||||
        threading.Thread(
 | 
			
		||||
            target=tpu_mtj_backend.infer_static,
 | 
			
		||||
            args=(np.uint32((23403, 727, 20185)),),
 | 
			
		||||
            kwargs={
 | 
			
		||||
                "soft_embeddings": vars.sp,
 | 
			
		||||
                "soft_tokens": soft_tokens,
 | 
			
		||||
                "gen_len": 1,
 | 
			
		||||
                "numseqs": vars.numseqs,
 | 
			
		||||
            },
 | 
			
		||||
        ).start()
 | 
			
		||||
 | 
			
		||||
#==================================================================#
 | 
			
		||||
#  Final startup commands to launch Flask app
 | 
			
		||||
#==================================================================#
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								bridge.lua
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								bridge.lua
									
									
									
									
									
								
							@@ -1851,13 +1851,14 @@ return function(_python, _bridged)
 | 
			
		||||
    -- API for aiserver.py
 | 
			
		||||
    --==========================================================================
 | 
			
		||||
 | 
			
		||||
    ---@return nil
 | 
			
		||||
    ---@return boolean
 | 
			
		||||
    function koboldbridge.load_userscripts(filenames, modulenames, descriptions)
 | 
			
		||||
        config_files = {}
 | 
			
		||||
        config_file_filename_map = {}
 | 
			
		||||
        koboldbridge.userscripts = {}
 | 
			
		||||
        koboldbridge.userscriptmodule_filename_map = {}
 | 
			
		||||
        koboldbridge.num_userscripts = 0
 | 
			
		||||
        local has_genmod = false
 | 
			
		||||
        for i, filename in _python.enumerate(filenames) do
 | 
			
		||||
            bridged.load_callback(filename, modulenames[i])
 | 
			
		||||
            koboldbridge.logging_name = modulenames[i]
 | 
			
		||||
@@ -1865,12 +1866,15 @@ return function(_python, _bridged)
 | 
			
		||||
            local f, err = old_loadfile(join_folder_and_filename(bridged.userscript_path, filename), "t", koboldbridge.get_universe(filename))
 | 
			
		||||
            if err ~= nil then
 | 
			
		||||
                error(err)
 | 
			
		||||
                return
 | 
			
		||||
                return false
 | 
			
		||||
            end
 | 
			
		||||
            ---@type KoboldUserScript
 | 
			
		||||
            local _userscript = f()
 | 
			
		||||
            koboldbridge.logging_name = nil
 | 
			
		||||
            koboldbridge.filename = nil
 | 
			
		||||
            if _userscript.genmod ~= nil then
 | 
			
		||||
                has_genmod = true
 | 
			
		||||
            end
 | 
			
		||||
            local userscript = deepcopy(KoboldUserScriptModule)
 | 
			
		||||
            rawset(userscript, "_inmod", function()
 | 
			
		||||
                koboldbridge.logging_name = modulenames[i]
 | 
			
		||||
@@ -1903,6 +1907,7 @@ return function(_python, _bridged)
 | 
			
		||||
            koboldbridge.userscriptmodule_filename_map[userscript] = filename
 | 
			
		||||
            koboldbridge.num_userscripts = i + 1
 | 
			
		||||
        end
 | 
			
		||||
        return has_genmod
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    ---@return nil
 | 
			
		||||
@@ -1949,7 +1954,9 @@ return function(_python, _bridged)
 | 
			
		||||
        koboldbridge.userstate = "genmod"
 | 
			
		||||
        if koboldbridge.genmod ~= nil then
 | 
			
		||||
            local _generated = deepcopy(koboldbridge.generated)
 | 
			
		||||
            r = koboldbridge.genmod()
 | 
			
		||||
            if not bridged.vars.nogenmod then
 | 
			
		||||
                r = koboldbridge.genmod()
 | 
			
		||||
            end
 | 
			
		||||
            setmetatable(koboldbridge.logits, nil)
 | 
			
		||||
            for kr, vr in old_next, koboldbridge.logits, nil do
 | 
			
		||||
                setmetatable(vr, nil)
 | 
			
		||||
 
 | 
			
		||||
@@ -162,6 +162,17 @@ gensettingstf = [{
 | 
			
		||||
	"step": 1,
 | 
			
		||||
	"default": 0,
 | 
			
		||||
    "tooltip": "When enabled, the Memory text box in the Random Story dialog will be prefilled by default with your current story's memory instead of being empty."
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
	"uitype": "toggle",
 | 
			
		||||
	"unit": "bool",
 | 
			
		||||
	"label": "No Genmod",
 | 
			
		||||
	"id": "setnogenmod",
 | 
			
		||||
	"min": 0,
 | 
			
		||||
	"max": 1,
 | 
			
		||||
	"step": 1,
 | 
			
		||||
	"default": 0,
 | 
			
		||||
    "tooltip": "Disables userscript generation modifiers."
 | 
			
		||||
	}]
 | 
			
		||||
 | 
			
		||||
gensettingsik =[{
 | 
			
		||||
 
 | 
			
		||||
@@ -2194,6 +2194,9 @@ $(document).ready(function(){
 | 
			
		||||
			if(!$("#setrngpersist").prop("checked")) {
 | 
			
		||||
				$("#rngmemory").val("");
 | 
			
		||||
			}
 | 
			
		||||
		} else if(msg.cmd == "updatenogenmod") {
 | 
			
		||||
			// Update toggle state
 | 
			
		||||
			$("#setnogenmod").prop('checked', msg.data).change();
 | 
			
		||||
		} else if(msg.cmd == "runs_remotely") {
 | 
			
		||||
			remote = true;
 | 
			
		||||
			hide([button_savetofile, button_import, button_importwi]);
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
	<script src="static/bootstrap.min.js"></script>
 | 
			
		||||
	<script src="static/bootstrap-toggle.min.js"></script>
 | 
			
		||||
	<script src="static/rangy-core.min.js"></script>
 | 
			
		||||
	<script src="static/application.js?ver=1.16.4v"></script>
 | 
			
		||||
	<script src="static/application.js?ver=1.16.4w"></script>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
	<input type="file" id="remote-save-select" accept="application/json" style="display:none">
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@ def __batch_xmap(shard_dim=1):
 | 
			
		||||
    return inner
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def apply_repetition_penalty(logits, tokens, repetition_penalty):
 | 
			
		||||
def apply_repetition_penalty_dynamic(logits, tokens, repetition_penalty):
 | 
			
		||||
    '''
 | 
			
		||||
    This gets called by generate_loop_fn to apply repetition penalty
 | 
			
		||||
    to the 1D array logits using the provided 1D array of tokens to penalize
 | 
			
		||||
@@ -85,7 +85,7 @@ def apply_repetition_penalty(logits, tokens, repetition_penalty):
 | 
			
		||||
    logits[tokens] = penalty_logits
 | 
			
		||||
    return logits
 | 
			
		||||
 | 
			
		||||
def kobold_sample(key, logits, top_p=0.9, temp=0.5, top_k=0, tfs=1.0):
 | 
			
		||||
def kobold_sample_dynamic(key, logits, top_p=0.9, temp=0.5, top_k=0, tfs=1.0):
 | 
			
		||||
    '''
 | 
			
		||||
    This gets called by generate_loop_fn to apply a series of 4 filters
 | 
			
		||||
    to the logits (top-k, then top-p, then TFS, then temperature) before
 | 
			
		||||
@@ -183,6 +183,127 @@ def kobold_sample(key, logits, top_p=0.9, temp=0.5, top_k=0, tfs=1.0):
 | 
			
		||||
    # probability distribution)
 | 
			
		||||
    return jax.random.categorical(key, logits, -1).astype(np.uint32)
 | 
			
		||||
 | 
			
		||||
def apply_repetition_penalty_static(logits, tokens, repetition_penalty):
 | 
			
		||||
    '''
 | 
			
		||||
    This gets called by generate_loop_fn to apply repetition penalty
 | 
			
		||||
    to the 1D array logits using the provided 1D array of tokens to penalize
 | 
			
		||||
    '''
 | 
			
		||||
    # Make a new array with the same length as the tokens array but with
 | 
			
		||||
    # each element replaced by the value at the corresponding index in the
 | 
			
		||||
    # logits array; e.g.
 | 
			
		||||
    # if logits is [77, 5, 3, 98] and tokens is [0, 1, 2, 3, 2, 3, 1],
 | 
			
		||||
    # then penalty_logits will be [77, 5, 3, 98, 3, 98, 5]
 | 
			
		||||
    penalty_logits = jnp.take(logits, tokens)
 | 
			
		||||
    # Divide positive values by repetition_penalty and multiply negative
 | 
			
		||||
    # values by repetition_penalty (the academic publication that described
 | 
			
		||||
    # this technique actually just only divided, but that would cause tokens
 | 
			
		||||
    # with negative logits to become more likely, which is obviously wrong)
 | 
			
		||||
    penalty_logits = jnp.where(
 | 
			
		||||
        penalty_logits > 0,
 | 
			
		||||
        penalty_logits/repetition_penalty,
 | 
			
		||||
        penalty_logits*repetition_penalty,
 | 
			
		||||
    )
 | 
			
		||||
    # Finally, put those penalized logit values back into their original
 | 
			
		||||
    # positions in the logits array
 | 
			
		||||
    return logits.at[tokens].set(penalty_logits)
 | 
			
		||||
 | 
			
		||||
def kobold_sample_static(key, logits, top_p=0.9, temp=0.5, top_k=0, tfs=1.0):
 | 
			
		||||
    '''
 | 
			
		||||
    This gets called by generate_loop_fn to apply a series of 4 filters
 | 
			
		||||
    to the logits (top-k, then top-p, then TFS, then temperature) before
 | 
			
		||||
    picking one token using the modified logits
 | 
			
		||||
    '''
 | 
			
		||||
    # Top-k (keep only the k tokens with the highest logits and remove
 | 
			
		||||
    # the rest, by setting their logits to negative infinity)
 | 
			
		||||
    def top_k_filter(logits):
 | 
			
		||||
        # After sorting the logits array in descending order,
 | 
			
		||||
        # sorted_indices_to_remove is a 1D array that is True for tokens
 | 
			
		||||
        # in the sorted logits array we want to remove and False for ones
 | 
			
		||||
        # we want to keep, in this case the first top_k elements will be
 | 
			
		||||
        # False and the rest will be True
 | 
			
		||||
        sorted_indices_to_remove = jnp.arange(len(logits)) >= top_k
 | 
			
		||||
        # Unsort the logits array back to its original configuration and
 | 
			
		||||
        # remove tokens we need to remove
 | 
			
		||||
        _, indices_to_remove = jax.lax.sort_key_val(
 | 
			
		||||
            jnp.argsort(-logits),
 | 
			
		||||
            sorted_indices_to_remove,
 | 
			
		||||
        )
 | 
			
		||||
        return jnp.where(indices_to_remove, -jnp.inf, logits)
 | 
			
		||||
    logits = jax.lax.cond(top_k > 0, top_k_filter, lambda x: x, logits)
 | 
			
		||||
    # Top-p (after sorting the remaining tokens again in descending order of
 | 
			
		||||
    # logit, remove the ones that have cumulative softmax probability
 | 
			
		||||
    # greater than p)
 | 
			
		||||
    def top_p_filter(logits):
 | 
			
		||||
        # Sort the logits array in descending order, replace every element
 | 
			
		||||
        # with e (Euler's number) to the power of that element, and divide
 | 
			
		||||
        # each element of the new array by the sum of the elements in the
 | 
			
		||||
        # new array
 | 
			
		||||
        sorted_logits = -jnp.sort(-logits)
 | 
			
		||||
        probabilities = jax.nn.softmax(sorted_logits)
 | 
			
		||||
        # Calculate cumulative_probabilities as the prefix-sum array of
 | 
			
		||||
        # probabilities
 | 
			
		||||
        cumulative_probabilities = jnp.cumsum(probabilities, axis=-1)
 | 
			
		||||
        # We want to remove tokens with cumulative probability higher
 | 
			
		||||
        # than top_p
 | 
			
		||||
        sorted_indices_to_remove = cumulative_probabilities > top_p
 | 
			
		||||
        # Don't ever remove the token with the highest logit, even if
 | 
			
		||||
        # the probability is higher than top_p
 | 
			
		||||
        sorted_indices_to_remove = sorted_indices_to_remove.at[0].set(False)
 | 
			
		||||
        # Unsort and remove
 | 
			
		||||
        _, indices_to_remove = jax.lax.sort_key_val(
 | 
			
		||||
            jnp.argsort(-logits),
 | 
			
		||||
            sorted_indices_to_remove,
 | 
			
		||||
        )
 | 
			
		||||
        return jnp.where(indices_to_remove, -jnp.inf, logits)
 | 
			
		||||
    logits = jax.lax.cond(top_p < 1.0, top_p_filter, lambda x: x, logits)
 | 
			
		||||
    # Tail free sampling (basically top-p a second time on remaining tokens
 | 
			
		||||
    # except it's the "cumulative normalized absolute second finite
 | 
			
		||||
    # differences of the softmax probabilities" instead of just the
 | 
			
		||||
    # cumulative softmax probabilities)
 | 
			
		||||
    def tail_free_filter(logits):
 | 
			
		||||
        # Sort in descending order
 | 
			
		||||
        sorted_logits = -jnp.sort(-logits)
 | 
			
		||||
        # Softmax again
 | 
			
		||||
        probabilities = jax.nn.softmax(sorted_logits)
 | 
			
		||||
        # Calculate the second finite differences of that array (i.e.
 | 
			
		||||
        # calculate the difference array and then calculate the difference
 | 
			
		||||
        # array of the difference array)
 | 
			
		||||
        d2 = jnp.diff(jnp.diff(probabilities))
 | 
			
		||||
        # Get the absolute values of all those second finite differences
 | 
			
		||||
        d2 = jnp.abs(d2)
 | 
			
		||||
        # Normalize (all elements in the array are divided by the sum of the
 | 
			
		||||
        # array's elements)
 | 
			
		||||
        d2 = d2 / d2.sum(axis=-1, keepdims=True)
 | 
			
		||||
        # Get the prefix-sum array
 | 
			
		||||
        cumulative_d2 = jnp.cumsum(d2, axis=-1)
 | 
			
		||||
        # We will remove the tokens with a cumulative normalized absolute
 | 
			
		||||
        # second finite difference larger than the TFS value
 | 
			
		||||
        sorted_indices_to_remove = cumulative_d2 > tfs
 | 
			
		||||
        # Don't remove the token with the highest logit
 | 
			
		||||
        sorted_indices_to_remove = sorted_indices_to_remove.at[0].set(False)
 | 
			
		||||
        # Since the d2 array has two fewer elements than the logits array,
 | 
			
		||||
        # we'll add two extra Trues to the end
 | 
			
		||||
        sorted_indices_to_remove = jnp.pad(
 | 
			
		||||
            sorted_indices_to_remove,
 | 
			
		||||
            (0, 2),
 | 
			
		||||
            constant_values=True,
 | 
			
		||||
        )
 | 
			
		||||
        # Unsort and remove
 | 
			
		||||
        _, indices_to_remove = jax.lax.sort_key_val(
 | 
			
		||||
            jnp.argsort(-logits),
 | 
			
		||||
            sorted_indices_to_remove,
 | 
			
		||||
        )
 | 
			
		||||
        return jnp.where(indices_to_remove, -jnp.inf, logits)
 | 
			
		||||
    logits = jax.lax.cond(tfs < 1.0, tail_free_filter, lambda x: x, logits)
 | 
			
		||||
    # Temperature (just divide the logits by the temperature)
 | 
			
		||||
    def temp_filter(logits):
 | 
			
		||||
        return logits / temp
 | 
			
		||||
    logits = jax.lax.cond(True, temp_filter, lambda x: x, logits)
 | 
			
		||||
    # Finally, pick one token using the softmax thingy again (it gives
 | 
			
		||||
    # an array whose elements sum to 1 so it can be used nicely as a
 | 
			
		||||
    # probability distribution)
 | 
			
		||||
    return jax.random.categorical(key, logits, -1).astype(jnp.uint32)
 | 
			
		||||
 | 
			
		||||
pad_token_id = 50256
 | 
			
		||||
 | 
			
		||||
def sample_func(data, key, numseqs_aux, badwords, repetition_penalty, sampler_options):
 | 
			
		||||
@@ -192,11 +313,11 @@ def sample_func(data, key, numseqs_aux, badwords, repetition_penalty, sampler_op
 | 
			
		||||
        generated, generated_index, logits, _ = carry[0][0]
 | 
			
		||||
        sample_key = carry[1]
 | 
			
		||||
        # Get the pseudo-random number generator key that will
 | 
			
		||||
        # be used by kobold_sample to randomly pick a token
 | 
			
		||||
        # be used by kobold_sample_dynamic to randomly pick a token
 | 
			
		||||
        sample_key, new_key = jax.random.split(sample_key, num=2)
 | 
			
		||||
        # Apply repetition penalty to all tokens that are
 | 
			
		||||
        # currently inside the "generated" array
 | 
			
		||||
        logits = apply_repetition_penalty(
 | 
			
		||||
        logits = apply_repetition_penalty_dynamic(
 | 
			
		||||
            logits,
 | 
			
		||||
            generated,
 | 
			
		||||
            repetition_penalty
 | 
			
		||||
@@ -205,11 +326,11 @@ def sample_func(data, key, numseqs_aux, badwords, repetition_penalty, sampler_op
 | 
			
		||||
        # their logits to negative infinity which effectively
 | 
			
		||||
        # makes their probabilities of being chosen zero
 | 
			
		||||
        logits[badwords] = -np.inf
 | 
			
		||||
        # Use the sampler (kobold_sample) to pick one token
 | 
			
		||||
        # Use the sampler (kobold_sample_dynamic) to pick one token
 | 
			
		||||
        # based on the logits array as a 0D uint32 array
 | 
			
		||||
        # (higher logit means higher probability of being
 | 
			
		||||
        # picked, non-linearly)
 | 
			
		||||
        next_token = kobold_sample(
 | 
			
		||||
        next_token = kobold_sample_dynamic(
 | 
			
		||||
            sample_key,
 | 
			
		||||
            logits,
 | 
			
		||||
            **sampler_options,
 | 
			
		||||
@@ -236,6 +357,100 @@ class PenalizingCausalTransformer(CausalTransformer):
 | 
			
		||||
    def __init__(self, config):
 | 
			
		||||
        # Initialize
 | 
			
		||||
        super().__init__(config)
 | 
			
		||||
        def generate_static(state, key, ctx, ctx_length, gen_length, numseqs_aux, sampler_options, soft_embeddings=None):
 | 
			
		||||
            numseqs = numseqs_aux.shape[0]
 | 
			
		||||
            # These are the tokens that we don't want the AI to ever write
 | 
			
		||||
            self.badwords = jnp.array([6880, 50256, 42496, 4613, 17414, 22039, 16410, 27, 29, 38430, 37922, 15913, 24618, 28725, 58, 47175, 36937, 26700, 12878, 16471, 37981, 5218, 29795, 13412, 45160, 3693, 49778, 4211, 20598, 36475, 33409, 44167, 32406, 29847, 29342, 42669, 685, 25787, 7359, 3784, 5320, 33994, 33490, 34516, 43734, 17635, 24293, 9959, 23785, 21737, 28401, 18161, 26358, 32509, 1279, 38155, 18189, 26894, 6927, 14610, 23834, 11037, 14631, 26933, 46904, 22330, 25915, 47934, 38214, 1875, 14692, 41832, 13163, 25970, 29565, 44926, 19841, 37250, 49029, 9609, 44438, 16791, 17816, 30109, 41888, 47527, 42924, 23984, 49074, 33717, 31161, 49082, 30138, 31175, 12240, 14804, 7131, 26076, 33250, 3556, 38381, 36338, 32756, 46581, 17912, 49146])
 | 
			
		||||
            @hk.transform
 | 
			
		||||
            def generate_sample(context, ctx_length):
 | 
			
		||||
                # Give the initial context to the transformer
 | 
			
		||||
                transformer = CausalTransformerShard(config)
 | 
			
		||||
                def generate_initial_scan_fn(sequence_index, _):
 | 
			
		||||
                    _, initial_state = transformer.generate_initial(context, ctx_length, soft_embeddings=soft_embeddings)
 | 
			
		||||
                    # The "generated" array will contain the tokens from the
 | 
			
		||||
                    # context as well as the tokens picked by the sampler at
 | 
			
		||||
                    # each stage, padded with a bunch of 50256s, so we know
 | 
			
		||||
                    # which tokens have to be repetition penalized
 | 
			
		||||
                    generated = jnp.pad(context, (0, config["seq"]), constant_values=pad_token_id)  # Let it start off with just the 2048 context tokens, plus some 50256s which will be eventually filled with sampler-chosen tokens
 | 
			
		||||
                    generated_index = config["seq"]
 | 
			
		||||
                    # Add that information to generate_loop_fn's starting state
 | 
			
		||||
                    initial_state = (generated, generated_index, sequence_index) + initial_state
 | 
			
		||||
                    return sequence_index+1, initial_state
 | 
			
		||||
                _, initial_states = jax.lax.scan(generate_initial_scan_fn, 0, None, numseqs)
 | 
			
		||||
                sample_key = initial_states[-1][0]
 | 
			
		||||
                initial_states = list(jax.tree_map(lambda x: x[i], initial_states[:-1]) for i in range(numseqs))
 | 
			
		||||
                # Get repetition penalty from the arguments
 | 
			
		||||
                repetition_penalty = sampler_options.pop('repetition_penalty', None)
 | 
			
		||||
                # This is the main generation loop
 | 
			
		||||
                def generate_loop_fn(carry):
 | 
			
		||||
                    # Unpack current generate_loop_fn state
 | 
			
		||||
                    generated, generated_index, sequence_index, next_token, decode_state = carry[0][0]
 | 
			
		||||
                    sample_key = carry[1]
 | 
			
		||||
                    # Get the pseudo-random number generator key that will
 | 
			
		||||
                    # be used by kobold_sample_static to randomly pick a token
 | 
			
		||||
                    sample_key, new_key = jax.random.split(sample_key)
 | 
			
		||||
                    # Give the context to the model and get the logits it
 | 
			
		||||
                    # spits out
 | 
			
		||||
                    # (a 2D array with 1 row and 50400 columns representing
 | 
			
		||||
                    # how strongly it thinks each of the 50257 tokens in its
 | 
			
		||||
                    # vocabulary should be appended to the context, followed
 | 
			
		||||
                    # by 143 apparently useless columns ???)
 | 
			
		||||
                    logits, new_state = transformer.generate_once(next_token, decode_state, soft_embeddings=soft_embeddings)
 | 
			
		||||
                    # Verify that logits does indeed have that many rows and
 | 
			
		||||
                    # columns (if you get an error here, pray for mercy)
 | 
			
		||||
                    assert logits.shape == (1, config["n_vocab"])
 | 
			
		||||
                    # Flatten it into a 1D array to make it easier to use
 | 
			
		||||
                    logits = logits[0]
 | 
			
		||||
                    # Apply repetition penalty to all tokens that are
 | 
			
		||||
                    # currently inside the "generated" array
 | 
			
		||||
                    if repetition_penalty is not None:
 | 
			
		||||
                        logits = apply_repetition_penalty_static(
 | 
			
		||||
                            logits,
 | 
			
		||||
                            generated,
 | 
			
		||||
                            repetition_penalty
 | 
			
		||||
                        )
 | 
			
		||||
                    # Remove any tokens in the badwords list by setting
 | 
			
		||||
                    # their logits to negative infinity which effectively
 | 
			
		||||
                    # makes their probabilities of being chosen zero
 | 
			
		||||
                    logits = logits.at[self.badwords].set(-jnp.inf)
 | 
			
		||||
                    # Use the sampler (kobold_sample_static) to pick one token
 | 
			
		||||
                    # based on the logits array as a 0D uint32 array
 | 
			
		||||
                    # (higher logit means higher probability of being
 | 
			
		||||
                    # picked, non-linearly)
 | 
			
		||||
                    next_token = kobold_sample_static(
 | 
			
		||||
                        sample_key,
 | 
			
		||||
                        logits,
 | 
			
		||||
                        **sampler_options,
 | 
			
		||||
                    )
 | 
			
		||||
                    # Remember what token was picked
 | 
			
		||||
                    generated = generated.at[generated_index].set(next_token)
 | 
			
		||||
                    generated_index += 1
 | 
			
		||||
                    # Re-pack the current generate_loop_fn's state so we can
 | 
			
		||||
                    # get back the same variables the next time
 | 
			
		||||
                    carry[0][0] = (generated, generated_index, sequence_index, next_token[jnp.newaxis], new_state)
 | 
			
		||||
                    carry[0].append(carry[0].pop(0))
 | 
			
		||||
                    return carry[0], new_key
 | 
			
		||||
                return jax.lax.while_loop(
 | 
			
		||||
                    lambda carry: carry[0][0][1] - config["seq"] < gen_length,
 | 
			
		||||
                    generate_loop_fn,
 | 
			
		||||
                    (initial_states, sample_key),
 | 
			
		||||
                )
 | 
			
		||||
            return generate_sample.apply(state["params"], key, ctx, ctx_length)
 | 
			
		||||
        self.generate_static_xmap = jax.experimental.maps.xmap(
 | 
			
		||||
            fun=generate_static,
 | 
			
		||||
            in_axes=(
 | 
			
		||||
                ["shard", ...],
 | 
			
		||||
                ["batch", ...],
 | 
			
		||||
                ["batch", ...],
 | 
			
		||||
                ["batch", ...],
 | 
			
		||||
                ["batch", ...],
 | 
			
		||||
                ["batch", ...],
 | 
			
		||||
                ["batch", ...],
 | 
			
		||||
                ["shard", ...],
 | 
			
		||||
            ),
 | 
			
		||||
            out_axes=["shard", "batch", ...],
 | 
			
		||||
            axis_resources={'shard': 'mp', 'batch': 'dp'},
 | 
			
		||||
        )
 | 
			
		||||
        def generate_initial(state, key, ctx, ctx_length, numseqs_aux, soft_embeddings=None):
 | 
			
		||||
            numseqs = numseqs_aux.shape[0]
 | 
			
		||||
            @hk.transform
 | 
			
		||||
@@ -314,7 +529,7 @@ class PenalizingCausalTransformer(CausalTransformer):
 | 
			
		||||
            out_axes=["shard", "batch", ...],
 | 
			
		||||
            axis_resources={'shard': 'mp', 'batch': 'dp'},
 | 
			
		||||
        )
 | 
			
		||||
    def generate(self, ctx, ctx_length, gen_length, numseqs, sampler_options, return_logits=False, soft_embeddings=None, excluded_world_info=None, use_callback=True):
 | 
			
		||||
    def generate_dynamic(self, ctx, ctx_length, gen_length, numseqs, sampler_options, return_logits=False, soft_embeddings=None, excluded_world_info=None, use_callback=True):
 | 
			
		||||
        assert excluded_world_info is not None
 | 
			
		||||
        assert not return_logits
 | 
			
		||||
        assert gen_length.ndim == 1
 | 
			
		||||
@@ -360,9 +575,24 @@ class PenalizingCausalTransformer(CausalTransformer):
 | 
			
		||||
            else:
 | 
			
		||||
                break
 | 
			
		||||
        return sample_data, n_generated, regeneration_required, halt
 | 
			
		||||
    def generate_static(self, ctx, ctx_length, gen_length, numseqs, sampler_options, return_logits=False, soft_embeddings=None):
 | 
			
		||||
        assert not return_logits
 | 
			
		||||
        key = hk.PRNGSequence(random.randint(0, 2 ** 60))
 | 
			
		||||
        batch_size = ctx.shape[0]
 | 
			
		||||
        self.batch_size = batch_size
 | 
			
		||||
        return self.generate_static_xmap(
 | 
			
		||||
            self.state,
 | 
			
		||||
            jnp.array(key.take(batch_size)),
 | 
			
		||||
            ctx,
 | 
			
		||||
            np.array(ctx_length, dtype=np.uint32),
 | 
			
		||||
            np.array(gen_length, dtype=np.uint32),
 | 
			
		||||
            np.empty((batch_size, numseqs), dtype=np.uint8),
 | 
			
		||||
            sampler_options,
 | 
			
		||||
            soft_embeddings,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def infer(
 | 
			
		||||
def infer_dynamic(
 | 
			
		||||
    context: np.array,
 | 
			
		||||
    top_p=0.9,
 | 
			
		||||
    temp=0.5,
 | 
			
		||||
@@ -394,7 +624,7 @@ def infer(
 | 
			
		||||
        "repetition_penalty": float(repetition_penalty),
 | 
			
		||||
        "top_k": int(top_k),
 | 
			
		||||
    }
 | 
			
		||||
    output = network.generate(
 | 
			
		||||
    output = network.generate_dynamic(
 | 
			
		||||
        batched_tokens,
 | 
			
		||||
        np.ones(total_batch, dtype=np.uint32) * provided_ctx,
 | 
			
		||||
        np.ones(total_batch, dtype=np.uint32) * gen_len,
 | 
			
		||||
@@ -408,6 +638,47 @@ def infer(
 | 
			
		||||
        samples.append(out[0][params["seq"] : params["seq"] + gen_len])
 | 
			
		||||
    return (samples,) + output[1:]
 | 
			
		||||
 | 
			
		||||
def infer_static(
 | 
			
		||||
    context: np.array,
 | 
			
		||||
    top_p=0.9,
 | 
			
		||||
    temp=0.5,
 | 
			
		||||
    top_k=0,
 | 
			
		||||
    tfs=1.0,
 | 
			
		||||
    repetition_penalty=1.0,
 | 
			
		||||
    numseqs=1,
 | 
			
		||||
    gen_len=80,
 | 
			
		||||
    soft_embeddings: Optional[np.array] = None,
 | 
			
		||||
    soft_tokens: Optional[np.array] = None,
 | 
			
		||||
) -> List[np.array]:
 | 
			
		||||
    maps.thread_resources.env = thread_resources_env
 | 
			
		||||
    total_batch = 1
 | 
			
		||||
    tokens = context
 | 
			
		||||
    if(soft_tokens is not None):
 | 
			
		||||
        tokens = np.uint32(np.concatenate((soft_tokens, tokens)))
 | 
			
		||||
    provided_ctx = tokens.shape[0]
 | 
			
		||||
    pad_amount = seq - provided_ctx
 | 
			
		||||
    padded_tokens = np.pad(tokens, ((pad_amount, 0),), constant_values=pad_token_id)
 | 
			
		||||
    batched_tokens = np.array([padded_tokens] * total_batch)
 | 
			
		||||
    samples = []
 | 
			
		||||
    batched_generator_params = {
 | 
			
		||||
        "temp": temp * np.ones(total_batch),
 | 
			
		||||
        "top_p": top_p * np.ones(total_batch),
 | 
			
		||||
        "tfs": tfs * np.ones(total_batch),
 | 
			
		||||
        "repetition_penalty": repetition_penalty * np.ones(total_batch),
 | 
			
		||||
        "top_k": np.full(total_batch, top_k, dtype=np.uint32)
 | 
			
		||||
    }
 | 
			
		||||
    output = network.generate_static(
 | 
			
		||||
        batched_tokens,
 | 
			
		||||
        np.ones(total_batch, dtype=np.uint32) * provided_ctx,
 | 
			
		||||
        np.ones(total_batch, dtype=np.uint32) * gen_len,
 | 
			
		||||
        numseqs,
 | 
			
		||||
        batched_generator_params,
 | 
			
		||||
        soft_embeddings=soft_embeddings,
 | 
			
		||||
    )[0]
 | 
			
		||||
    for o in output:
 | 
			
		||||
        samples.append(o[0][0, 0, params["seq"] : params["seq"] + gen_len])
 | 
			
		||||
    return samples
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load_model(path: str, driver_version="tpu_driver0.1_dev20210607", **kwargs) -> None:
 | 
			
		||||
    global thread_resources_env, seq, tokenizer, network, params
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user