Brave como respaldo de DuckDuckGo, y arreglo de la guardia de bf16
Cadena de búsqueda con respaldo. DuckDuckGo sigue de primario porque no exige credenciales: en el camino habitual ningún tercero se entera de qué busca la familia. Pero su endpoint lite no es una API con contrato, así que cuando limite por tasa o cambie el HTML, Brave responde. Dos matices del encadenado: - Cero resultados NO dispara el respaldo. Si el primario respondió bien y no encontró nada, esa es la respuesta correcta; encadenar gastaría cuota y devolvería resultados peores. Solo se avanza ante un error real. - La cadena recuerda quién respondió: al depurar una respuesta rara, lo primero que hay que saber es de dónde salió. Un respaldo sin credencial falla al arrancar y no en la primera consulta — que es justo cuando el primario ya falló y el respaldo tiene que funcionar. Aparte, verificando torch en la 2060 apareció un defecto real en backends.py: torch.cuda.is_bf16_supported() usa including_emulation=True por defecto, así que devuelve True en Turing, donde bf16 no existe en silicio. La guardia no habría atrapado el perfil de la 5090 corriendo en la 2060: el entrenamiento seguía, emulado y silencioso. Medido en la placa: 4,49 ms por matmul en bf16 contra 1,75 ms en fp16, unas 2,6x más lento. Ahora se consulta con including_emulation=False, con respaldo por compute capability. 11 tests nuevos (113 en total). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+87
-8
@@ -5,27 +5,97 @@
|
||||
# servidor y no se descargan enteros. Solo vuelven artefactos chicos: métricas,
|
||||
# muestras generadas y logs.
|
||||
#
|
||||
# Dos direcciones para la misma máquina: la de LAN se prueba primero porque es
|
||||
# el caso habitual y es mucho más rápida para rsync; si no responde, se cae a la
|
||||
# pública. Forzar una con ENLACE_REMOTE_FORCE=lan|wan.
|
||||
#
|
||||
# scripts/remote.sh check # conectividad, GPU y entorno
|
||||
# scripts/remote.sh sync # empuja el código al servidor
|
||||
# scripts/remote.sh run <config.yaml> [args] # entrena bajo tmux
|
||||
# scripts/remote.sh logs [corrida] # sigue el log en vivo
|
||||
# scripts/remote.sh pull <corrida> # trae métricas y muestras
|
||||
# scripts/remote.sh gpu # estado de la GPU
|
||||
# scripts/remote.sh ssh [comando] # shell o comando suelto
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
[[ -f .env ]] && set -a && source .env && set +a
|
||||
|
||||
HOST="${ENLACE_REMOTE_HOST:?definí ENLACE_REMOTE_HOST en .env (ver .env.example)}"
|
||||
DIR="${ENLACE_REMOTE_DIR:?definí ENLACE_REMOTE_DIR en .env}"
|
||||
DIR="${ENLACE_REMOTE_DIR:?definí ENLACE_REMOTE_DIR en .env (ver .env.example)}"
|
||||
PY="${ENLACE_REMOTE_PYTHON:-$DIR/.venv/bin/python}"
|
||||
|
||||
# Timeout corto: si no estamos en la LAN, no tiene sentido esperar el TCP.
|
||||
LAN_TIMEOUT="${ENLACE_LAN_TIMEOUT:-3}"
|
||||
SSH_OPTS=(-o BatchMode=yes -o StrictHostKeyChecking=accept-new)
|
||||
|
||||
_alcanzable() {
|
||||
ssh "${SSH_OPTS[@]}" -o "ConnectTimeout=$1" "$2" true 2>/dev/null
|
||||
}
|
||||
|
||||
elegir_host() {
|
||||
local lan="${ENLACE_REMOTE_LAN_HOST:-}" wan="${ENLACE_REMOTE_HOST:-}"
|
||||
|
||||
case "${ENLACE_REMOTE_FORCE:-}" in
|
||||
lan) echo "${lan:?ENLACE_REMOTE_FORCE=lan pero ENLACE_REMOTE_LAN_HOST no está definido}"; return ;;
|
||||
wan) echo "${wan:?ENLACE_REMOTE_FORCE=wan pero ENLACE_REMOTE_HOST no está definido}"; return ;;
|
||||
esac
|
||||
|
||||
if [[ -n "$lan" ]] && _alcanzable "$LAN_TIMEOUT" "$lan"; then
|
||||
echo "$lan"
|
||||
return
|
||||
fi
|
||||
if [[ -n "$wan" ]]; then
|
||||
echo "$wan"
|
||||
return
|
||||
fi
|
||||
echo "definí ENLACE_REMOTE_LAN_HOST o ENLACE_REMOTE_HOST en .env" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
HOST="$(elegir_host)"
|
||||
# A stderr para no contaminar la salida de los comandos que se parsean.
|
||||
if [[ "$HOST" == "${ENLACE_REMOTE_LAN_HOST:-}" ]]; then
|
||||
echo "[enlace] servidor: $HOST (LAN)" >&2
|
||||
else
|
||||
echo "[enlace] servidor: $HOST (remoto)" >&2
|
||||
fi
|
||||
|
||||
comando="${1:-}"
|
||||
shift || true
|
||||
|
||||
case "$comando" in
|
||||
check)
|
||||
# Todo lo que tiene que estar bien antes de comprometer horas de cómputo o
|
||||
# gigabytes de corpus. Falla ruidoso y temprano.
|
||||
ssh "${SSH_OPTS[@]}" "$HOST" bash -s -- "$DIR" "$PY" <<'REMOTO'
|
||||
set -u
|
||||
DIR="$1"; PY="$2"
|
||||
echo "host: $(hostname)"
|
||||
echo "repo: $DIR $( [ -d "$DIR/.git" ] && echo '(ok)' || echo '(FALTA: cloná el repo)')"
|
||||
echo "python: $( [ -x "$PY" ] && "$PY" --version 2>&1 || echo 'FALTA el venv')"
|
||||
echo "tmux: $(command -v tmux >/dev/null && tmux -V || echo 'FALTA (las corridas largas lo necesitan)')"
|
||||
if command -v nvidia-smi >/dev/null; then
|
||||
nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv,noheader | sed 's/^/gpu: /'
|
||||
else
|
||||
echo "gpu: nvidia-smi no está en PATH"
|
||||
fi
|
||||
[ -x "$PY" ] && "$PY" - <<'PY'
|
||||
try:
|
||||
import torch
|
||||
except ImportError:
|
||||
print("torch: no instalado (corré `remote.sh sync`)"); raise SystemExit
|
||||
print(f"torch: {torch.__version__} | cuda disponible: {torch.cuda.is_available()}")
|
||||
if torch.cuda.is_available():
|
||||
cap = torch.cuda.get_device_capability(0)
|
||||
print(f" sm_{cap[0]}{cap[1]} | bf16: {torch.cuda.is_bf16_supported()}")
|
||||
print(f" perfil sugerido: {'blackwell-5090' if cap >= (12,0) else 'turing-2060'}")
|
||||
PY
|
||||
REMOTO
|
||||
;;
|
||||
|
||||
sync)
|
||||
git push
|
||||
ssh "$HOST" "cd '$DIR' && git pull --ff-only && $PY -m pip install -q -e ."
|
||||
ssh "${SSH_OPTS[@]}" "$HOST" "cd '$DIR' && git pull --ff-only && $PY -m pip install -q -e ."
|
||||
;;
|
||||
|
||||
run)
|
||||
@@ -35,7 +105,8 @@ run)
|
||||
# distintas no se pisan y `logs` sabe a cuál conectarse.
|
||||
sesion="enlace-$(basename "$config" .yaml)"
|
||||
# tmux es lo que hace que cortar el SSH no mate el entrenamiento.
|
||||
ssh -t "$HOST" "cd '$DIR' && tmux new-session -d -s '$sesion' \
|
||||
ssh "${SSH_OPTS[@]}" -t "$HOST" "cd '$DIR' && mkdir -p runs && \
|
||||
tmux new-session -d -s '$sesion' \
|
||||
\"$PY -m enlace.train.train '$config' $* 2>&1 | tee -a 'runs/$sesion.log'\" \
|
||||
&& echo 'corriendo en tmux: $sesion'"
|
||||
;;
|
||||
@@ -43,9 +114,9 @@ run)
|
||||
logs)
|
||||
sesion="${1:-}"
|
||||
if [[ -z "$sesion" ]]; then
|
||||
ssh "$HOST" "cd '$DIR' && ls -t runs/*.log | head -1 | xargs tail -f"
|
||||
ssh "${SSH_OPTS[@]}" "$HOST" "cd '$DIR' && ls -t runs/*.log | head -1 | xargs tail -f"
|
||||
else
|
||||
ssh "$HOST" "cd '$DIR' && tail -f 'runs/$sesion.log'"
|
||||
ssh "${SSH_OPTS[@]}" "$HOST" "cd '$DIR' && tail -f 'runs/$sesion.log'"
|
||||
fi
|
||||
;;
|
||||
|
||||
@@ -58,11 +129,19 @@ pull)
|
||||
;;
|
||||
|
||||
gpu)
|
||||
ssh "$HOST" "nvidia-smi"
|
||||
ssh "${SSH_OPTS[@]}" "$HOST" "nvidia-smi"
|
||||
;;
|
||||
|
||||
ssh)
|
||||
if [[ $# -eq 0 ]]; then
|
||||
ssh "${SSH_OPTS[@]}" -t "$HOST" "cd '$DIR' && exec \$SHELL -l"
|
||||
else
|
||||
ssh "${SSH_OPTS[@]}" "$HOST" "cd '$DIR' && $*"
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
sed -n '2,14p' "$0"
|
||||
sed -n '2,20p' "$0"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user